diff --git a/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp b/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp index 2d87eb68..7e7ccc53 100644 --- a/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp @@ -1,117 +1,119 @@ /* This file is part of the KDE project Copyright (C) 2007 Lukas Appelhans Copyright (C) 2007 Joris Guisson 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. */ #include "btadvanceddetailswidget.h" #include #include #include "bttransferhandler.h" #include "bittorrentsettings.h" #include "fileview.h" #include "chunkdownloadview.h" #include "peerview.h" #include "monitor.h" #include "trackerview.h" #include "webseedstab.h" #include "kget_debug.h" -#include -#include -#include -#include -#include + +#include +#include +#include +#include + +#include #include using namespace kt; BTAdvancedDetailsWidget::BTAdvancedDetailsWidget(BTTransferHandler * transfer) : m_transfer(transfer) { tc = m_transfer->torrentControl(); init(); //This updates the widget with the right values slotTransferChanged(transfer, 0xFFFFFFFF); connect(m_transfer, SIGNAL(transferChangedEvent(TransferHandler*,TransferHandler::ChangesFlags)), this, SLOT(slotTransferChanged(TransferHandler*,TransferHandler::ChangesFlags))); } void BTAdvancedDetailsWidget::init() { setWindowTitle(i18n("Advanced Details for %1", m_transfer->source().fileName())); resize(500, 400); QGridLayout *layout = new QGridLayout(); KTitleWidget *titleWidget = new KTitleWidget(this); titleWidget->setText(i18n("Advanced Details for %1", m_transfer->source().fileName())); titleWidget->setPixmap(QIcon::fromTheme("dialog-information")); layout->addWidget(titleWidget); tabWidget = new KTabWidget(this); layout->addWidget(tabWidget); setLayout(layout); file_view = new FileView(this); file_view->changeTC(tc, KSharedConfig::openConfig()); tabWidget->insertTab(0, file_view, QIcon::fromTheme("inode-directory"), i18n("Files")); //peer_view = new PeerView(this); //tabWidget->insertTab(1, peer_view, QIcon::fromTheme("system-users"), i18n("Peers")); //cd_view = new ChunkDownloadView(this); //cd_view->changeTC(tc); //tabWidget->insertTab(2, cd_view, QIcon::fromTheme("preferences-plugin"), i18n("Chunks")); tracker_view = new TrackerView(this); tracker_view->changeTC(tc); tabWidget->insertTab(1, tracker_view, QIcon::fromTheme("network-server"), i18n("Trackers")); webseeds_tab = new WebSeedsTab(this); webseeds_tab->changeTC(tc); tabWidget->insertTab(2, webseeds_tab, QIcon::fromTheme("network-server"), i18n("Webseeds")); monitor = new Monitor(tc, nullptr, nullptr, file_view); } void BTAdvancedDetailsWidget::hideEvent(QHideEvent * event) { Q_UNUSED(event) if (tc) tc->setMonitor(0); emit aboutToClose(); deleteLater(); } kt::Monitor* BTAdvancedDetailsWidget::torrentMonitor() const { return monitor; } void BTAdvancedDetailsWidget::slotTransferChanged(TransferHandler * transfer, TransferHandler::ChangesFlags flags) { qCDebug(KGET_DEBUG) << "BTAdvancedDetailsWidget::slotTransferChanged" ; Q_UNUSED(transfer) if (flags & BTTransfer::Tc_ChunksTotal || flags & BTTransfer::Tc_ChunksDownloaded || flags & BTTransfer::Tc_ChunksExcluded || flags & BTTransfer::Tc_ChunksLeft || flags & Transfer::Tc_DownloadSpeed || flags & Transfer::Tc_UploadSpeed) { //if (tabWidget->currentIndex() == 1) // peer_view->update(); //else if (tabWidget->currentIndex() == 2) // cd_view->update(); /*else */if (tabWidget->currentIndex() == 1) tracker_view->update(); } /*else if (m_transfer->status() == Job::Stopped) { peer_view->removeAll(); //cd_view->removeAll(); }*/ } diff --git a/transfer-plugins/bittorrent/advanceddetails/chunkdownloadmodel.cpp b/transfer-plugins/bittorrent/advanceddetails/chunkdownloadmodel.cpp index b179bd03..24ec74eb 100644 --- a/transfer-plugins/bittorrent/advanceddetails/chunkdownloadmodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/chunkdownloadmodel.cpp @@ -1,293 +1,294 @@ /*************************************************************************** * 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 "chunkdownloadmodel.h" -#include #include #include #include #include +#include + using namespace bt; namespace kt { ChunkDownloadModel::Item::Item(ChunkDownloadInterface* cd,const QString & files) : cd(cd),files(files) { cd->getStats(stats); } bool ChunkDownloadModel::Item::changed(int col,bool & modified) const { ChunkDownloadInterface::Stats s; cd->getStats(s); bool ret = false; switch (col) { case 1: ret = s.pieces_downloaded != stats.pieces_downloaded; break; case 2: ret = s.current_peer_id != stats.current_peer_id; break; case 3: ret = s.download_speed != stats.download_speed; break; default: break; } modified = s.pieces_downloaded != stats.pieces_downloaded || s.download_speed != stats.download_speed || s.current_peer_id != stats.current_peer_id; stats = s; return ret; } QVariant ChunkDownloadModel::Item::data(int col) const { switch (col) { case 0: return stats.chunk_index; case 1: return QString("%1 / %2").arg(stats.pieces_downloaded).arg(stats.total_pieces); case 2: return stats.current_peer_id; case 3: return BytesPerSecToString(stats.download_speed); case 4: return files; } return QVariant(); } bool ChunkDownloadModel::Item::lessThan(int col,const Item* other) const { switch (col) { case 0: return stats.chunk_index < other->stats.chunk_index; case 1: return stats.pieces_downloaded < other->stats.pieces_downloaded; case 2: return stats.current_peer_id < other->stats.current_peer_id; case 3: return stats.download_speed < other->stats.download_speed; case 4: return files < other->files; } return false; } ///////////////////////////////////////////////////////////// ChunkDownloadModel::ChunkDownloadModel ( QObject* parent ) : QAbstractTableModel(parent),tc(nullptr) { sort_column = 0; sort_order = Qt::AscendingOrder; } ChunkDownloadModel::~ChunkDownloadModel() { qDeleteAll(items); } void ChunkDownloadModel::downloadAdded(bt::ChunkDownloadInterface* cd) { if (!tc) return; bt::ChunkDownloadInterface::Stats stats; cd->getStats(stats); QString files; int n = 0; if (tc->getStats().multi_file_torrent) { for (Uint32 i = 0;i < tc->getNumFiles();++i) { const bt::TorrentFileInterface & tf = tc->getTorrentFile(i); if (stats.chunk_index >= tf.getFirstChunk() && stats.chunk_index <= tf.getLastChunk()) { if (n > 0) files += '\n'; files += tf.getPath(); n++; } else if (stats.chunk_index < tf.getFirstChunk()) break; } } Item* nitem = new Item(cd,files); items.append(nitem); insertRow(items.count() - 1); sort(sort_column,sort_order); } void ChunkDownloadModel::downloadRemoved(bt::ChunkDownloadInterface* cd) { int idx = 0; for (QList::iterator i = items.begin();i != items.end();i++) { const Item* item = *i; if (item->cd == cd) { items.erase(i); delete item; removeRow(idx); break; } idx++; } } void ChunkDownloadModel::changeTC(bt::TorrentInterface* tc) { qDeleteAll(items); items.clear(); this->tc = tc; reset(); } void ChunkDownloadModel::clear() { qDeleteAll(items); items.clear(); reset(); } void ChunkDownloadModel::update() { bool resort = false; Uint32 idx=0; foreach (Item* i,items) { bool modified = false; if (i->changed(sort_column,modified)) resort = true; if (modified && !resort) emit dataChanged(index(idx,1),index(idx,3)); idx++; } if (resort) sort(sort_column,sort_order); } int ChunkDownloadModel::rowCount(const QModelIndex & parent) const { if (parent.isValid()) return 0; else return items.count(); } int ChunkDownloadModel::columnCount(const QModelIndex & parent) const { if (parent.isValid()) return 0; else return 5; } QVariant ChunkDownloadModel::headerData(int section,Qt::Orientation orientation,int role) const { if (orientation != Qt::Horizontal) return QVariant(); if (role == Qt::DisplayRole) { switch (section) { case 0: return i18n("Chunk"); case 1: return i18n("Progress"); case 2: return i18n("Peer"); case 3: return i18n("Down Speed"); case 4: return i18n("Files"); default: return QVariant(); } } else if (role == Qt::ToolTipRole) { switch (section) { case 0: return i18n("Number of the chunk"); case 1: return i18n("Download progress of the chunk"); case 2: return i18n("Which peer we are downloading it from"); case 3: return i18n("Download speed of the chunk"); case 4: return i18n("Which files the chunk is located in"); default: return QVariant(); } } return QVariant(); } QModelIndex ChunkDownloadModel::index(int row,int column,const QModelIndex & parent) const { if (!hasIndex(row,column,parent) || parent.isValid()) return QModelIndex(); else return createIndex(row,column,items[row]); } QVariant ChunkDownloadModel::data(const QModelIndex & index,int role) const { if (!index.isValid() || index.row() >= items.count() || index.row() < 0) return QVariant(); if (role == Qt::DisplayRole) return items[index.row()]->data(index.column()); return QVariant(); } bool ChunkDownloadModel::removeRows(int row,int count,const QModelIndex & /*parent*/ ) { beginRemoveRows(QModelIndex(),row,row + count - 1); endRemoveRows(); return true; } bool ChunkDownloadModel::insertRows(int row,int count,const QModelIndex & /*parent*/ ) { beginInsertRows(QModelIndex(),row,row + count - 1); endInsertRows(); return true; } class ChunkDownloadModelItemCmp { public: ChunkDownloadModelItemCmp(int col,Qt::SortOrder order) : col(col),order(order) {} bool operator()(ChunkDownloadModel::Item* a,ChunkDownloadModel::Item* b) { if (order == Qt::AscendingOrder) return a->lessThan(col,b); else return !a->lessThan(col,b); } int col; Qt::SortOrder order; }; void ChunkDownloadModel::sort(int col, Qt::SortOrder order) { sort_column = col; sort_order = order; emit layoutAboutToBeChanged(); qStableSort(items.begin(),items.end(),ChunkDownloadModelItemCmp(col,order)); emit layoutChanged(); } } diff --git a/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.cpp b/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.cpp index b498ecec..7efe1894 100644 --- a/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.cpp @@ -1,121 +1,122 @@ /*************************************************************************** * 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 "chunkdownloadview.h" #include #include #include -#include +#include + #include #include #include #include #include #include "chunkdownloadmodel.h" using namespace bt; namespace kt { ChunkDownloadView::ChunkDownloadView(QWidget* parent) : QWidget(parent),curr_tc(nullptr) { setupUi(this); model = new ChunkDownloadModel(this); m_chunk_view->setModel(model); m_chunk_view->setRootIsDecorated(false); m_chunk_view->setSortingEnabled(true); m_chunk_view->setAlternatingRowColors(true); m_chunk_view->setUniformRowHeights(true); } ChunkDownloadView::~ChunkDownloadView() { } void ChunkDownloadView::downloadAdded(ChunkDownloadInterface* cd) { model->downloadAdded(cd); } void ChunkDownloadView::downloadRemoved(ChunkDownloadInterface* cd) { model->downloadRemoved(cd); } void ChunkDownloadView::update() { if (!curr_tc) return; model->update(); const TorrentStats & s = curr_tc->getStats(); m_chunks_downloading->setText(QString::number(s.num_chunks_downloading)); m_chunks_downloaded->setText(QString::number(s.num_chunks_downloaded)); m_excluded_chunks->setText(QString::number(s.num_chunks_excluded)); m_chunks_left->setText(QString::number(s.num_chunks_left)); } void ChunkDownloadView::changeTC(TorrentInterface* tc) { curr_tc = tc; if (!curr_tc) { setEnabled(false); } else { setEnabled(true); const TorrentStats & s = curr_tc->getStats(); m_total_chunks->setText(QString::number(s.total_chunks)); m_size_chunks->setText(BytesToString(s.chunk_size)); } model->changeTC(tc); } void ChunkDownloadView::removeAll() { model->clear(); } void ChunkDownloadView::saveState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("ChunkDownloadView"); QByteArray s = m_chunk_view->header()->saveState(); g.writeEntry("state",s.toBase64()); } void ChunkDownloadView::loadState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("ChunkDownloadView"); QByteArray s = QByteArray::fromBase64(g.readEntry("state",QByteArray())); if (!s.isNull()) { QHeaderView* v = m_chunk_view->header(); v->restoreState(s); m_chunk_view->sortByColumn(v->sortIndicatorSection(),v->sortIndicatorOrder()); model->sort(v->sortIndicatorSection(),v->sortIndicatorOrder()); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.h b/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.h index 1b9071f8..46474c13 100644 --- a/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.h +++ b/transfer-plugins/bittorrent/advanceddetails/chunkdownloadview.h @@ -1,73 +1,73 @@ /*************************************************************************** * Copyright (C) 2007 by Joris Guisson * * joris.guisson@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 KT_CHUNKDOWNLOADVIEW_HH #define KT_CHUNKDOWNLOADVIEW_HH - #include -#include +#include + #include #include "ui_chunkdownloadview.h" namespace bt { class TorrentInterface; } namespace kt { class ChunkDownloadModel; /** * View which shows a list of downloading chunks, of a torrent. * */ class ChunkDownloadView : public QWidget,public Ui_ChunkDownloadView { Q_OBJECT public: ChunkDownloadView(QWidget* parent); virtual ~ChunkDownloadView(); /// A peer has been added void downloadAdded(bt::ChunkDownloadInterface* cd); /// A download has been removed void downloadRemoved(bt::ChunkDownloadInterface* cd); /// Check to see if the GUI needs to be updated void update(); /// Change the torrent to display void changeTC(bt::TorrentInterface* tc); /// Remove all items void removeAll(); void saveState(KSharedConfigPtr cfg); void loadState(KSharedConfigPtr cfg); private: bt::TorrentInterface* curr_tc; ChunkDownloadModel* model; }; } #endif diff --git a/transfer-plugins/bittorrent/advanceddetails/fileview.cpp b/transfer-plugins/bittorrent/advanceddetails/fileview.cpp index 6fd8db3c..9e82c3f1 100644 --- a/transfer-plugins/bittorrent/advanceddetails/fileview.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/fileview.cpp @@ -1,468 +1,468 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@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 "fileview.h" +#include #include #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include + +#include +#include +#include +#include +#include +#include +#include + #include #include #include #include #include -#include #include #include #include "iwfiletreemodel.h" #include "iwfilelistmodel.h" using namespace bt; namespace kt { FileView::FileView(QWidget *parent) : QTreeView(parent),curr_tc(0),model(0) { setContextMenuPolicy(Qt::CustomContextMenu); setRootIsDecorated(false); setSortingEnabled(true); setAlternatingRowColors(true); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionBehavior(QAbstractItemView::SelectRows); setUniformRowHeights(true); proxy_model = new QSortFilterProxyModel(this); proxy_model->setSortRole(Qt::UserRole); setModel(proxy_model); context_menu = new QMenu(this); open_action = context_menu->addAction(QIcon::fromTheme("document-open"),i18nc("Open file", "Open"),this,SLOT(open())); context_menu->addSeparator(); download_first_action = context_menu->addAction(i18n("Download first"),this,SLOT(downloadFirst())); download_normal_action = context_menu->addAction(i18n("Download normally"),this,SLOT(downloadNormal())); download_last_action = context_menu->addAction(i18n("Download last"),this,SLOT(downloadLast())); context_menu->addSeparator(); dnd_action = context_menu->addAction(i18n("Do Not Download"),this,SLOT(doNotDownload())); delete_action = context_menu->addAction(i18n("Delete File(s)"),this,SLOT(deleteFiles())); context_menu->addSeparator(); move_files_action = context_menu->addAction(i18n("Move File"),this,SLOT(moveFiles())); context_menu->addSeparator(); collapse_action = context_menu->addAction(i18n("Collapse Folder Tree"),this,SLOT(collapseTree())); expand_action = context_menu->addAction(i18n("Expand Folder Tree"),this,SLOT(expandTree())); connect(this,SIGNAL(customContextMenuRequested(QPoint)), this,SLOT(showContextMenu(QPoint))); connect(this,SIGNAL(doubleClicked(QModelIndex)), this,SLOT(onDoubleClicked(QModelIndex))); setEnabled(false); show_list_of_files = false; redraw = false; } FileView::~FileView() {} void FileView::changeTC(bt::TorrentInterface* tc,KSharedConfigPtr cfg) { if (tc == curr_tc) return; if (model) { saveState(cfg); if (curr_tc) expanded_state_map[curr_tc] = model->saveExpandedState(proxy_model,this); } proxy_model->setSourceModel(0); delete model; model = nullptr; curr_tc = tc; setEnabled(tc != nullptr); if (tc) { connect(tc,SIGNAL(missingFilesMarkedDND(bt::TorrentInterface*)), this,SLOT(onMissingFileMarkedDND(bt::TorrentInterface*))); if (show_list_of_files) model = new IWFileListModel(tc,this); else model = new IWFileTreeModel(tc,this); proxy_model->setSourceModel(model); setRootIsDecorated(tc->getStats().multi_file_torrent); loadState(cfg); QMap::iterator i = expanded_state_map.find(tc); if (i != expanded_state_map.end()) model->loadExpandedState(proxy_model,this,i.value()); else expandAll(); } else { proxy_model->setSourceModel(0); model = nullptr; } } void FileView::onMissingFileMarkedDND(bt::TorrentInterface* tc) { if (curr_tc == tc) model->missingFilesMarkedDND(); } void FileView::showContextMenu(const QPoint & p) { const TorrentStats & s = curr_tc->getStats(); QModelIndexList sel = selectionModel()->selectedRows(); if (sel.count() == 0) return; if (sel.count() > 1) { download_first_action->setEnabled(true); download_normal_action->setEnabled(true); download_last_action->setEnabled(true); open_action->setEnabled(false); dnd_action->setEnabled(true); delete_action->setEnabled(true); context_menu->popup(mapToGlobal(p)); move_files_action->setEnabled(true); collapse_action->setEnabled(!show_list_of_files); expand_action->setEnabled(!show_list_of_files); return; } QModelIndex item = proxy_model->mapToSource(sel.front()); bt::TorrentFileInterface* file = model->indexToFile(item); download_first_action->setEnabled(false); download_last_action->setEnabled(false); download_normal_action->setEnabled(false); dnd_action->setEnabled(false); delete_action->setEnabled(false); if (!s.multi_file_torrent) { open_action->setEnabled(true); move_files_action->setEnabled(true); preview_path = curr_tc->getStats().output_path; collapse_action->setEnabled(false); expand_action->setEnabled(false); } else if (file) { move_files_action->setEnabled(true); collapse_action->setEnabled(false); expand_action->setEnabled(false); if (!file->isNull()) { open_action->setEnabled(true); preview_path = file->getPathOnDisk(); download_first_action->setEnabled(file->getPriority() != FIRST_PRIORITY); download_normal_action->setEnabled(file->getPriority() != NORMAL_PRIORITY); download_last_action->setEnabled(file->getPriority() != LAST_PRIORITY); dnd_action->setEnabled(file->getPriority() != ONLY_SEED_PRIORITY); delete_action->setEnabled(file->getPriority() != EXCLUDED); } else { open_action->setEnabled(false); } } else { move_files_action->setEnabled(false); download_first_action->setEnabled(true); download_normal_action->setEnabled(true); download_last_action->setEnabled(true); dnd_action->setEnabled(true); delete_action->setEnabled(true); open_action->setEnabled(true); preview_path = curr_tc->getDataDir() + model->dirPath(item); collapse_action->setEnabled(!show_list_of_files); expand_action->setEnabled(!show_list_of_files); } context_menu->popup(mapToGlobal(p)); } void FileView::open() { new KRun(QUrl(preview_path), nullptr, true); } void FileView::changePriority(bt::Priority newpriority) { QModelIndexList sel = selectionModel()->selectedRows(2); for (QModelIndexList::iterator i = sel.begin();i != sel.end();++i) *i = proxy_model->mapToSource(*i); model->changePriority(sel,newpriority); proxy_model->invalidate(); } void FileView::downloadFirst() { changePriority(FIRST_PRIORITY); } void FileView::downloadLast() { changePriority(LAST_PRIORITY); } void FileView::downloadNormal() { changePriority(NORMAL_PRIORITY); } void FileView::doNotDownload() { changePriority(ONLY_SEED_PRIORITY); } void FileView::deleteFiles() { QModelIndexList sel = selectionModel()->selectedRows(); Uint32 n = sel.count(); if (n == 1) // single item can be a directory { if (!model->indexToFile(proxy_model->mapToSource(sel.front()))) ++n; } QString msg = i18np("You will lose all data in this file, are you sure you want to do this?", "You will lose all data in these files, are you sure you want to do this?", n); if (KMessageBox::warningYesNo(nullptr, msg) == KMessageBox::Yes) changePriority(EXCLUDED); } void FileView::moveFiles() { if (curr_tc->getStats().multi_file_torrent) { QModelIndexList sel = selectionModel()->selectedRows(); QMap moves; QString dir = KFileDialog::getExistingDirectory(QUrl("kfiledialog:///saveTorrentData"), this,i18n("Select a directory to move the data to.")); if (dir.isNull()) return; foreach (const QModelIndex &idx,sel) { bt::TorrentFileInterface* tfi = model->indexToFile(proxy_model->mapToSource(idx)); if (!tfi) continue; moves.insert(tfi,dir); } if (moves.count() > 0) { curr_tc->moveTorrentFiles(moves); } } else { QString dir = KFileDialog::getExistingDirectory(QUrl("kfiledialog:///saveTorrentData"), this,i18n("Select a directory to move the data to.")); if (dir.isNull()) return; curr_tc->changeOutputDir(dir,bt::TorrentInterface::MOVE_FILES); } } void FileView::expandCollapseTree(const QModelIndex& idx, bool expand) { int rowCount = proxy_model->rowCount(idx); for (int i = 0; i < rowCount; i++) { const QModelIndex& ridx = proxy_model->index(i, 0, idx); if (proxy_model->hasChildren(ridx)) expandCollapseTree(ridx, expand); } setExpanded(idx, expand); } void FileView::expandCollapseSelected(bool expand) { QModelIndexList sel = selectionModel()->selectedRows(); for (QModelIndexList::iterator i = sel.begin(); i != sel.end(); ++i) { if (proxy_model->hasChildren(*i)) expandCollapseTree(*i, expand); } } void FileView::collapseTree() { expandCollapseSelected(false); } void FileView::expandTree() { expandCollapseSelected(true); } void FileView::onDoubleClicked(const QModelIndex & index) { if (!curr_tc) return; const TorrentStats & s = curr_tc->getStats(); if (s.multi_file_torrent) { bt::TorrentFileInterface* file = model->indexToFile(proxy_model->mapToSource(index)); if (!file) { // directory new KRun(QUrl(curr_tc->getDataDir() + model->dirPath(proxy_model->mapToSource(index))), nullptr, true); } else { // file new KRun(QUrl(file->getPathOnDisk()), nullptr, true); } } else { new KRun(QUrl(curr_tc->getStats().output_path), nullptr, true); } } void FileView::saveState(KSharedConfigPtr cfg) { if (!model) return; KConfigGroup g = cfg->group("FileView"); QByteArray s = header()->saveState(); g.writeEntry("state",s.toBase64()); } void FileView::loadState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("FileView"); QByteArray s = QByteArray::fromBase64(g.readEntry("state",QByteArray())); if (!s.isNull()) { QHeaderView* v = header(); v->restoreState(s); sortByColumn(v->sortIndicatorSection(),v->sortIndicatorOrder()); } } void FileView::update() { if (model) model->update(); if (redraw) { scheduleDelayedItemsLayout(); redraw = false; } } void FileView::onTorrentRemoved(bt::TorrentInterface* tc) { expanded_state_map.remove(tc); } void FileView::setShowListOfFiles(bool on,KSharedConfigPtr cfg) { if (show_list_of_files == on) return; show_list_of_files = on; if (!model || !curr_tc) return; saveState(cfg); expanded_state_map[curr_tc] = model->saveExpandedState(proxy_model,this); proxy_model->setSourceModel(0); delete model; model = nullptr; if (show_list_of_files) model = new IWFileListModel(curr_tc,this); else model = new IWFileTreeModel(curr_tc,this); proxy_model->setSourceModel(model); setRootIsDecorated(curr_tc->getStats().multi_file_torrent); loadState(cfg); QMap::iterator i = expanded_state_map.find(curr_tc); if (i != expanded_state_map.end()) model->loadExpandedState(proxy_model,this,i.value()); else expandAll(); collapse_action->setEnabled(!show_list_of_files); expand_action->setEnabled(!show_list_of_files); } bool FileView::viewportEvent(QEvent *event) { executeDelayedItemsLayout(); return QTreeView::viewportEvent(event); } void FileView::filePercentageChanged(bt::TorrentFileInterface* file,float percentage) { if (model) model->filePercentageChanged(file,percentage); } void FileView::filePreviewChanged(bt::TorrentFileInterface* file,bool preview) { if (model) model->filePreviewChanged(file,preview); } void FileView::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight) { Q_UNUSED(topLeft) Q_UNUSED(bottomRight) redraw = true; } } diff --git a/transfer-plugins/bittorrent/advanceddetails/fileview.h b/transfer-plugins/bittorrent/advanceddetails/fileview.h index f53b18d6..48ea241e 100644 --- a/transfer-plugins/bittorrent/advanceddetails/fileview.h +++ b/transfer-plugins/bittorrent/advanceddetails/fileview.h @@ -1,108 +1,109 @@ /*************************************************************************** * Copyright (C) 2005-2007 by Joris Guisson * * joris.guisson@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 KTFILEVIEW_H #define KTFILEVIEW_H -#include #include -#include + +#include +#include class QMenu; class QSortFilterProxyModel; namespace bt { class TorrentInterface; class TorrentFileInterface; } namespace kt { class TorrentFileModel; /** @author Joris Guisson */ class FileView : public QTreeView { Q_OBJECT public: FileView(QWidget *parent); virtual ~FileView(); void changeTC(bt::TorrentInterface* tc,KSharedConfigPtr cfg); void setShowListOfFiles(bool on,KSharedConfigPtr cfg); void saveState(KSharedConfigPtr cfg); void loadState(KSharedConfigPtr cfg); void update(); void filePercentageChanged(bt::TorrentFileInterface* file,float percentage); void filePreviewChanged(bt::TorrentFileInterface* file,bool preview); public slots: void onTorrentRemoved(bt::TorrentInterface* tc); private slots: void showContextMenu(const QPoint & p); void onDoubleClicked(const QModelIndex & index); void onMissingFileMarkedDND(bt::TorrentInterface* tc); private: void changePriority(bt::Priority newpriority); void expandCollapseTree(const QModelIndex& idx, bool expand); void expandCollapseSelected(bool expand); virtual bool viewportEvent(QEvent *event); virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); private slots: void open(); void downloadFirst(); void downloadLast(); void downloadNormal(); void doNotDownload(); void deleteFiles(); void moveFiles(); void collapseTree(); void expandTree(); private: bool redraw; bt::TorrentInterface* curr_tc; TorrentFileModel* model; QMenu* context_menu; QAction* open_action; QAction* download_first_action; QAction* download_normal_action; QAction* download_last_action; QAction* dnd_action; QAction* delete_action; QAction* move_files_action; QAction* collapse_action; QAction* expand_action; QString preview_path; bool show_list_of_files; QMap expanded_state_map; QSortFilterProxyModel* proxy_model; }; } #endif diff --git a/transfer-plugins/bittorrent/advanceddetails/iwfilelistmodel.cpp b/transfer-plugins/bittorrent/advanceddetails/iwfilelistmodel.cpp index 2ac226a2..d3049e2e 100644 --- a/transfer-plugins/bittorrent/advanceddetails/iwfilelistmodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/iwfilelistmodel.cpp @@ -1,284 +1,285 @@ /*************************************************************************** * 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 "iwfilelistmodel.h" -#include -#include -#include +#include + +#include + #include #include #include using namespace bt; namespace kt { IWFileListModel::IWFileListModel(bt::TorrentInterface* tc,QObject* parent) : TorrentFileListModel(tc,KEEP_FILES,parent) { mmfile = IsMultimediaFile(tc->getStats().output_path); preview = false; percentage = 0; } IWFileListModel::~IWFileListModel() { } int IWFileListModel::columnCount(const QModelIndex & parent) const { if (!parent.isValid()) return 5; else return 0; } QVariant IWFileListModel::headerData(int section, Qt::Orientation orientation,int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); if (section < 2) return TorrentFileListModel::headerData(section,orientation,role); switch (section) { case 2: return i18n("Priority"); case 3: return i18n("Preview"); // xgettext: no-c-format case 4: return i18nc("Percent of File Downloaded", "% Complete"); default: return QVariant(); } } static QString PriorityString(const bt::TorrentFileInterface* file) { switch(file->getPriority()) { case FIRST_PRIORITY: return i18nc("Download first", "First"); case LAST_PRIORITY: return i18nc("Download last", "Last"); case ONLY_SEED_PRIORITY: case EXCLUDED: case PREVIEW_PRIORITY: return QString(); default:return i18nc("Download Normal (not as first or last)", "Normal"); } } QVariant IWFileListModel::data(const QModelIndex & index, int role) const { if (index.column() < 2 && role != Qt::ForegroundRole) return TorrentFileListModel::data(index,role); if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(QModelIndex())) return QVariant(); if (role == Qt::ForegroundRole && index.column() == 2 && tc->getStats().multi_file_torrent) { const bt::TorrentFileInterface* file = &tc->getTorrentFile(index.row()); switch (file->getPriority()) { /*case FIRST_PRIORITY: return InfoWidgetPluginSettings::firstColor(); case LAST_PRIORITY: return InfoWidgetPluginSettings::lastColor(); case NORMAL_PRIORITY: return InfoWidgetPluginSettings::normalColor(); case ONLY_SEED_PRIORITY: case EXCLUDED: case PREVIEW_PRIORITY:*/ default: return QVariant(); } } if (role == Qt::DisplayRole) return displayData(index); else if (role == Qt::UserRole) return sortData(index); return QVariant(); } QVariant IWFileListModel::displayData(const QModelIndex & index) const { if (tc->getStats().multi_file_torrent) { const bt::TorrentFileInterface* file = &tc->getTorrentFile(index.row()); switch (index.column()) { case 2: return PriorityString(file); case 3: if (file->isMultimedia()) { if (file->isPreviewAvailable()) return i18nc("Preview available", "Available"); else return i18nc("Preview pending", "Pending"); } else return i18nc("No preview available", "No"); case 4: { float percent = file->getDownloadPercentage(); return ki18n("%1 %").subs(percent, 0, 'f', 2).toString(); } default: return QVariant(); } } else { switch (index.column()) { case 2: return QVariant(); case 3: if (mmfile) { if (tc->readyForPreview()) return i18nc("Preview available", "Available"); else return i18nc("Preview pending", "Pending"); } else return i18nc("No preview available", "No"); case 4: { double percent = bt::Percentage(tc->getStats()); return ki18n("%1 %").subs(percent, 0, 'f', 2).toString(); } default: return QVariant(); } } return QVariant(); } QVariant IWFileListModel::sortData(const QModelIndex & index) const { if (tc->getStats().multi_file_torrent) { const bt::TorrentFileInterface* file = &tc->getTorrentFile(index.row()); switch (index.column()) { case 2: return (int)file->getPriority(); case 3: if (file->isMultimedia()) { if (file->isPreviewAvailable()) return 3; else return 2; } else return 1; case 4: return file->getDownloadPercentage(); } } else { switch (index.column()) { case 2: return QVariant(); case 3: if (mmfile) { if (tc->readyForPreview()) return 3; else return 2; } else return 1; case 4: return bt::Percentage(tc->getStats()); } } return QVariant(); } bool IWFileListModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (role == Qt::CheckStateRole) return TorrentFileListModel::setData(index,value,role); if (!index.isValid() || role != Qt::UserRole) return false; int r = index.row(); if (r < 0 || r >= rowCount(QModelIndex())) return false; bt::TorrentFileInterface & file = tc->getTorrentFile(r);; Priority prio = (bt::Priority)value.toInt(); Priority old = file.getPriority(); if (prio != old) { file.setPriority(prio); dataChanged(createIndex(index.row(),0),createIndex(index.row(),4)); } return true; } void IWFileListModel::filePercentageChanged(bt::TorrentFileInterface* file,float percentage) { Q_UNUSED(percentage) QModelIndex idx = createIndex(file->getIndex(),4,file); emit dataChanged(idx,idx); } void IWFileListModel::filePreviewChanged(bt::TorrentFileInterface* file,bool preview) { Q_UNUSED(preview) QModelIndex idx = createIndex(file->getIndex(),3,file); emit dataChanged(idx,idx); } void IWFileListModel::update() { if (!tc->getStats().multi_file_torrent) { bool changed = false; bool np = mmfile && tc->readyForPreview(); if (preview != np) { preview = np; changed = true; } double perc = bt::Percentage(tc->getStats()); if (fabs(perc - percentage) > 0.01) { percentage = perc; changed = true; } if (changed) dataChanged(createIndex(0,0),createIndex(0,4)); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/iwfiletreemodel.cpp b/transfer-plugins/bittorrent/advanceddetails/iwfiletreemodel.cpp index f77c2934..68c0fbe9 100644 --- a/transfer-plugins/bittorrent/advanceddetails/iwfiletreemodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/iwfiletreemodel.cpp @@ -1,345 +1,345 @@ /*************************************************************************** * 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 "iwfiletreemodel.h" -#include -#include -#include +#include +#include + #include #include #include using namespace bt; namespace kt { IWFileTreeModel::IWFileTreeModel(bt::TorrentInterface* tc,QObject* parent) : TorrentFileTreeModel(tc,KEEP_FILES,parent) { mmfile = IsMultimediaFile(tc->getStats().output_path); preview = false; percentage = 0; if (root) { BitSet d = tc->downloadedChunksBitSet(); d -= tc->onlySeedChunksBitSet(); root->initPercentage(tc,d); } } IWFileTreeModel::~IWFileTreeModel() { } int IWFileTreeModel::columnCount(const QModelIndex & /*parent*/) const { return 5; } QVariant IWFileTreeModel::headerData(int section, Qt::Orientation orientation,int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); if (section < 2) return TorrentFileTreeModel::headerData(section,orientation,role); switch (section) { case 2: return i18n("Priority"); case 3: return i18n("Preview"); // xgettext: no-c-format case 4: return i18nc("Percent of File Downloaded", "% Complete"); default: return QVariant(); } } static QString PriorityString(const bt::TorrentFileInterface* file) { switch(file->getPriority()) { case FIRST_PRIORITY: return i18nc("Download first", "First"); case LAST_PRIORITY: return i18nc("Download last", "Last"); case ONLY_SEED_PRIORITY: case EXCLUDED: case PREVIEW_PRIORITY: return QString(); default:return i18nc("Download normally(not as first or last)", "Normal"); } } QVariant IWFileTreeModel::data(const QModelIndex & index, int role) const { Node* n = nullptr; if (index.column() < 2 && role != Qt::ForegroundRole) return TorrentFileTreeModel::data(index,role); if (!index.isValid() || !(n = (Node*)index.internalPointer())) return QVariant(); if (role == Qt::ForegroundRole && index.column() == 2 && tc->getStats().multi_file_torrent && n->file) { const bt::TorrentFileInterface* file = n->file; switch (file->getPriority()) { /*case FIRST_PRIORITY: return InfoWidgetPluginSettings::firstColor(); case LAST_PRIORITY: return InfoWidgetPluginSettings::lastColor(); case NORMAL_PRIORITY: return InfoWidgetPluginSettings::normalColor(); case ONLY_SEED_PRIORITY: case EXCLUDED: case PREVIEW_PRIORITY:*/ default: return QVariant(); } } if (role == Qt::DisplayRole) return displayData(n,index); else if (role == Qt::UserRole) return sortData(n,index); return QVariant(); } QVariant IWFileTreeModel::displayData(Node* n,const QModelIndex & index) const { if (tc->getStats().multi_file_torrent && n->file) { const bt::TorrentFileInterface* file = n->file; switch (index.column()) { case 2: return PriorityString(file); case 3: if (file->isMultimedia()) { if (file->isPreviewAvailable()) return i18nc("preview available", "Available"); else return i18nc("Preview pending", "Pending"); } else return i18nc("No preview available", "No"); case 4: if (file->getPriority() == ONLY_SEED_PRIORITY || file->getPriority() == EXCLUDED) return QVariant(); else return ki18n("%1 %").subs(n->percentage, 0, 'f', 2).toString(); default: return QVariant(); } } else if (!tc->getStats().multi_file_torrent) { switch (index.column()) { case 2: return QVariant(); case 3: if (mmfile) { if (tc->readyForPreview()) return i18nc("Preview available", "Available"); else return i18nc("Preview pending", "Pending"); } else return i18nc("No preview available", "No"); case 4: return ki18n("%1 %").subs(bt::Percentage(tc->getStats()), 0, 'f', 2).toString(); default: return QVariant(); } } else if (tc->getStats().multi_file_torrent && index.column() == 4) { return ki18n("%1 %").subs(n->percentage, 0, 'f', 2).toString(); } return QVariant(); } QVariant IWFileTreeModel::sortData(Node* n,const QModelIndex & index) const { if (tc->getStats().multi_file_torrent && n->file) { const bt::TorrentFileInterface* file = n->file; switch (index.column()) { case 2: return (int)file->getPriority(); case 3: if (file->isMultimedia()) { if (file->isPreviewAvailable()) return 3; else return 2; } else return 1; case 4: return n->percentage; } } else if (!tc->getStats().multi_file_torrent) { switch (index.column()) { case 2: return QVariant(); case 3: if (mmfile) { if (tc->readyForPreview()) return 3; else return 2; } else return 1; case 4: return bt::Percentage(tc->getStats()); } } else if (tc->getStats().multi_file_torrent && index.column() == 4) { return n->percentage; } return QVariant(); } bool IWFileTreeModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (role == Qt::CheckStateRole) return TorrentFileTreeModel::setData(index,value,role); if (!index.isValid() || role != Qt::UserRole) return false; Node* n = static_cast(index.internalPointer()); if (!n) return false; if (!n->file) { for (int i = 0;i < n->children.count();i++) { // recurse down the tree setData(index.child(i,0),value,role); } } else { bt::TorrentFileInterface* file = n->file; Priority prio = (bt::Priority)value.toInt(); Priority old = file->getPriority(); if (prio != old) { file->setPriority(prio); dataChanged(createIndex(index.row(),0),createIndex(index.row(),4)); QModelIndex parent = index.parent(); if (parent.isValid()) dataChanged(parent,parent); // parent needs to be updated to } } return true; } void IWFileTreeModel::filePercentageChanged(bt::TorrentFileInterface* file,float percentage) { Q_UNUSED(percentage) update(index(0,0,QModelIndex()),file,4); } void IWFileTreeModel::filePreviewChanged(bt::TorrentFileInterface* file,bool preview) { Q_UNUSED(preview) update(index(0,0,QModelIndex()),file,3); } void IWFileTreeModel::update(const QModelIndex & idx,bt::TorrentFileInterface* file,int col) { Node* n = (Node*)idx.internalPointer(); if (n->file && n->file == file) { QModelIndex i = createIndex(idx.row(),col,n); emit dataChanged(i,i); if(col == 4) { // update percentages along the tree // this will go back up the tree and update the percentage of // all directories involved BitSet d = tc->downloadedChunksBitSet(); d -= tc->onlySeedChunksBitSet(); n->updatePercentage(d); // emit necessary signals QModelIndex parent = idx.parent(); while (parent.isValid()) { Node* nd = (Node*)parent.internalPointer(); i = createIndex(parent.row(),4,nd); emit dataChanged(i,i); parent = parent.parent(); } } } else { for (int i = 0;i < n->children.count();i++) { // recurse down the tree update(idx.child(i,0),file,col); } } } void IWFileTreeModel::update() { if (!tc->getStats().multi_file_torrent) { bool changed = false; bool np = mmfile && tc->readyForPreview(); if (preview != np) { preview = np; changed = true; } double perc = bt::Percentage(tc->getStats()); if (fabs(perc - percentage) > 0.01) { percentage = perc; changed = true; } if (changed) dataChanged(createIndex(0,2),createIndex(0,4)); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/peerview.cpp b/transfer-plugins/bittorrent/advanceddetails/peerview.cpp index be001a3e..1775708b 100644 --- a/transfer-plugins/bittorrent/advanceddetails/peerview.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/peerview.cpp @@ -1,138 +1,140 @@ /*************************************************************************** * 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 "peerview.h" #include -#include #include #include -#include -#include + +#include +#include +#include + #include #include #include #include "peerviewmodel.h" using namespace bt; namespace kt { PeerView::PeerView(QWidget* parent) : QTreeView(parent) { setContextMenuPolicy(Qt::CustomContextMenu); setRootIsDecorated(false); setSortingEnabled(true); setAlternatingRowColors(true); setUniformRowHeights(true); model = new PeerViewModel(this); setModel(model); context_menu = new QMenu(this); context_menu->addAction(QIcon::fromTheme("list-remove-user"),i18n("Kick Peer"),this,SLOT(kickPeer())); context_menu->addAction(QIcon::fromTheme("view-filter"),i18n("Ban Peer"),this,SLOT(banPeer())); connect(this,SIGNAL(customContextMenuRequested(QPoint)), this,SLOT(showContextMenu(QPoint))); } PeerView::~PeerView() { } void PeerView::showContextMenu(const QPoint& pos) { if (selectionModel()->selectedRows().count() == 0) return; context_menu->popup(mapToGlobal(pos)); } void PeerView::banPeer() { AccessManager & aman = AccessManager::instance(); QModelIndexList indices = selectionModel()->selectedRows(); foreach (const QModelIndex &idx,indices) { bt::PeerInterface* peer = model->indexToPeer(idx); if (peer) { aman.banPeer(peer->getStats().ip_address); peer->kill(); } } } void PeerView::kickPeer() { QModelIndexList indices = selectionModel()->selectedRows(); foreach (const QModelIndex &idx,indices) { bt::PeerInterface* peer = model->indexToPeer(idx); if (peer) peer->kill(); } } void PeerView::peerAdded(PeerInterface* peer) { model->peerAdded(peer); } void PeerView::peerRemoved(PeerInterface* peer) { model->peerRemoved(peer); } void PeerView::update() { model->update(); } void PeerView::removeAll() { model->clear(); } void PeerView::saveState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("PeerView"); QByteArray s = header()->saveState(); g.writeEntry("state",s.toBase64()); } void PeerView::loadState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("PeerView"); QByteArray s = QByteArray::fromBase64(g.readEntry("state",QByteArray())); if (!s.isNull()) { QHeaderView* v = header(); v->restoreState(s); sortByColumn(v->sortIndicatorSection(),v->sortIndicatorOrder()); model->sort(v->sortIndicatorSection(),v->sortIndicatorOrder()); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/peerview.h b/transfer-plugins/bittorrent/advanceddetails/peerview.h index 70e25f22..1245f63f 100644 --- a/transfer-plugins/bittorrent/advanceddetails/peerview.h +++ b/transfer-plugins/bittorrent/advanceddetails/peerview.h @@ -1,71 +1,71 @@ /*************************************************************************** * Copyright (C) 2007 by Joris Guisson * * joris.guisson@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 KT_PEERVIEW_HH #define KT_PEERVIEW_HH - #include +#include + #include #include -#include class QMenu; namespace kt { class PeerViewModel; /** * View which shows a list of peers, of a torrent. * */ class PeerView : public QTreeView { Q_OBJECT public: PeerView(QWidget* parent); virtual ~PeerView(); /// A peer has been added void peerAdded(bt::PeerInterface* peer); /// A peer has been removed void peerRemoved(bt::PeerInterface* peer); /// Check to see if the GUI needs to be updated void update(); /// Remove all items void removeAll(); void saveState(KSharedConfigPtr cfg); void loadState(KSharedConfigPtr cfg); private slots: void showContextMenu(const QPoint& pos); void banPeer(); void kickPeer(); private: QMenu* context_menu; PeerViewModel* model; }; } #endif diff --git a/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.cpp b/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.cpp index 7c7884ae..77ae62f4 100644 --- a/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.cpp @@ -1,413 +1,417 @@ /*************************************************************************** * Copyright (C) 2008 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 "peerviewmodel.h" -#include + +#include +#include + #include -#include +#include + #include #include #include using namespace bt; namespace kt { static QIcon yes,no; static bool icons_loaded = false; PeerViewModel::Item::Item(bt::PeerInterface* peer) : peer(peer) { stats = peer->getStats(); if (!icons_loaded) { yes = QIcon::fromTheme("dialog-ok"); no = QIcon::fromTheme("dialog-cancel"); } } /* bool PeerViewModel::Item::changed() const { const PeerInterface::Stats & s = peer->getStats(); if (s.download_rate != stats.download_rate || s.upload_rate != stats.upload_rate || s.choked != stats.choked || s.snubbed != stats.snubbed || s.perc_of_file != stats.perc_of_file || s.aca_score != stats.aca_score || s.has_upload_slot != stats.has_upload_slot || s.num_down_requests != stats.num_down_requests || s.num_up_requests != stats.num_up_requests || s.bytes_downloaded != stats.bytes_downloaded || s.bytes_uploaded != stats.bytes_uploaded || s.interested != stats.interested || s.am_interested != stats.am_interested) { stats = s; return true; } return false; } */ bool PeerViewModel::Item::changed(int col,bool & modified) const { const PeerInterface::Stats & s = peer->getStats(); bool ret = false; switch (col) { case 3: ret = s.download_rate != stats.download_rate; break; case 4: ret = s.upload_rate != stats.upload_rate; break; case 5: ret = s.choked != stats.choked; break; case 6: ret = s.snubbed != stats.snubbed; break; case 7: ret = s.perc_of_file != stats.perc_of_file; break; case 9: ret = s.aca_score != stats.aca_score; break; case 10: ret = s.has_upload_slot != stats.has_upload_slot; break; case 11: ret = (s.num_down_requests != stats.num_down_requests || s.num_up_requests != stats.num_up_requests); break; case 12: ret = s.bytes_downloaded != stats.bytes_downloaded; break; case 13: ret = s.bytes_uploaded != stats.bytes_uploaded; break; case 14: ret = s.interested != stats.interested; break; case 15: ret = s.am_interested != stats.am_interested; break; default: ret = false; break; } modified = s.download_rate != stats.download_rate || s.upload_rate != stats.upload_rate || s.choked != stats.choked || s.snubbed != stats.snubbed || s.perc_of_file != stats.perc_of_file || s.aca_score != stats.aca_score || s.has_upload_slot != stats.has_upload_slot || s.num_down_requests != stats.num_down_requests || s.num_up_requests != stats.num_up_requests || s.bytes_downloaded != stats.bytes_downloaded || s.bytes_uploaded != stats.bytes_uploaded || s.interested != stats.interested || s.am_interested != stats.am_interested; stats = s; return ret; } QVariant PeerViewModel::Item::data(int col) const { switch (col) { case 0: return stats.ip_address; case 1: return stats.client; case 2: if (stats.download_rate >= 103) return BytesPerSecToString(stats.download_rate); else return QVariant(); case 3: if (stats.upload_rate >= 103) return BytesPerSecToString(stats.upload_rate); else return QVariant(); case 4: return stats.choked ? i18nc("Choked", "Yes") : i18nc("Not choked", "No"); case 5: return stats.snubbed ? i18nc("Snubbed", "Yes") : i18nc("Not snubbed", "No"); - case 6: return QString("%1 %").arg(KLocale::global()->formatNumber(stats.perc_of_file,2)); + case 6: return QString("%1 %").arg(QLocale().toString(stats.perc_of_file, 'g', 2)); case 7: return QVariant(); - case 8: return KLocale::global()->formatNumber(stats.aca_score,2); + case 8: return QLocale().toString(stats.aca_score, 'g', 2); case 9: return QVariant(); case 10: return QString("%1 / %2").arg(stats.num_down_requests).arg(stats.num_up_requests); case 11: return BytesToString(stats.bytes_downloaded); case 12: return BytesToString(stats.bytes_uploaded); case 13: return stats.interested ? i18nc("Interested", "Yes") : i18nc("Not Interested", "No"); case 14: return stats.am_interested ? i18nc("Interesting", "Yes") : i18nc("Not Interesting", "No"); default: return QVariant(); } return QVariant(); } bool PeerViewModel::Item::lessThan(int col,const Item* other) const { switch (col) { case 0: return stats.ip_address < other->stats.ip_address; case 1: return QString::localeAwareCompare(stats.client,other->stats.client) < 0; case 2: return stats.download_rate < other->stats.download_rate; case 3: return stats.upload_rate < other->stats.upload_rate; case 4: return stats.choked < other->stats.choked; case 5: return stats.snubbed < other->stats.snubbed; case 6: return stats.perc_of_file < other->stats.perc_of_file; case 7: return stats.dht_support < other->stats.dht_support; case 8: return stats.aca_score < other->stats.aca_score; case 9: return stats.has_upload_slot < other->stats.has_upload_slot; case 10: return stats.num_down_requests + stats.num_up_requests < other->stats.num_down_requests + other->stats.num_up_requests; case 11: return stats.bytes_downloaded < other->stats.bytes_downloaded; case 12: return stats.bytes_uploaded < other->stats.bytes_uploaded; case 13: return stats.interested < other->stats.interested; case 14: return stats.am_interested < other->stats.am_interested; default: return false; } return false; } QVariant PeerViewModel::Item::decoration(int col) const { switch (col) { case 0: if (stats.encrypted) return QIcon::fromTheme("kt-encrypted"); break; case 1: return flag; case 8: return stats.dht_support ? yes : no; case 10: return stats.has_upload_slot ? yes : QIcon(); } return QVariant(); } ///////////////////////////////////////////////////////////// PeerViewModel::PeerViewModel ( QObject* parent ) : QAbstractTableModel(parent) { sort_column = 0; sort_order = Qt::AscendingOrder; } PeerViewModel::~PeerViewModel() { qDeleteAll(items); } void PeerViewModel::peerAdded(bt::PeerInterface* peer) { items.append(new Item(peer)); insertRow(items.count() - 1); sort(sort_column,sort_order); } void PeerViewModel::peerRemoved(bt::PeerInterface* peer) { int idx = 0; for (QList::iterator i = items.begin();i != items.end();i++) { Item* item = *i; if (item->peer == peer) { items.erase(i); delete item; removeRow(idx); break; } idx++; } } void PeerViewModel::clear() { qDeleteAll(items); items.clear(); reset(); } void PeerViewModel::update() { bool resort = false; Uint32 idx=0; foreach (Item* i,items) { bool modified = false; if (i->changed(sort_column,modified)) resort = true; if (modified && !resort) emit dataChanged(index(idx,3),index(idx,15)); idx++; } if (resort) sort(sort_column,sort_order); } QModelIndex PeerViewModel::index(int row,int column,const QModelIndex & parent) const { if (!hasIndex(row,column,parent) || parent.isValid()) return QModelIndex(); else return createIndex(row,column,items[row]); } int PeerViewModel::rowCount( const QModelIndex & parent) const { if (parent.isValid()) return 0; else return items.count(); } int PeerViewModel::columnCount(const QModelIndex & parent) const { if (parent.isValid()) return 0; else return 16; } QVariant PeerViewModel::headerData(int section, Qt::Orientation orientation,int role) const { if (orientation != Qt::Horizontal) return QVariant(); if (role == Qt::DisplayRole) { switch (section) { case 0: return i18n("IP Address"); case 1: return i18n("Client"); case 2: return i18n("Down Speed"); case 3: return i18n("Up Speed"); case 4: return i18n("Choked"); case 5: return i18n("Snubbed"); case 6: return i18n("Availability"); case 7: return i18n("DHT"); case 8: return i18n("Score"); case 9: return i18n("Upload Slot"); case 10: return i18n("Requests"); case 11: return i18n("Downloaded"); case 12: return i18n("Uploaded"); case 13: return i18n("Interested"); case 14: return i18n("Interesting"); default: return QVariant(); } } else if (role == Qt::ToolTipRole) { switch (section) { case 0: return i18n("IP address of the peer"); case 1: return i18n("Which client the peer is using"); case 2: return i18n("Download speed"); case 3: return i18n("Upload speed"); case 4: return i18n("Whether or not the peer has choked us. If we are choked, the peer will not send us any data."); case 5: return i18n("Snubbed means the peer has not sent us any data in the last 2 minutes"); case 6: return i18n("How much of the torrent's data the peer has"); case 7: return i18n("Whether or not the peer has DHT enabled"); case 8: return i18n("The score of the peer. KTorrent uses this to determine who to upload to."); case 9: return i18n("Only peers which have an upload slot will get data from us"); case 10: return i18n("The number of download and upload requests"); case 11: return i18n("How much data we have downloaded from this peer"); case 12: return i18n("How much data we have uploaded to this peer"); case 13: return i18n("Whether the peer is interested in downloading data from us"); case 14: return i18n("Whether we are interested in downloading from this peer"); default: return QVariant(); } } return QVariant(); } QVariant PeerViewModel::data(const QModelIndex & index,int role) const { if (!index.isValid() || index.row() >= items.count() || index.row() < 0) return QVariant(); Item* item = (Item*)index.internalPointer(); if (role == Qt::DisplayRole) return item->data(index.column()); else if (role == Qt::DecorationRole) return item->decoration(index.column()); return QVariant(); } bool PeerViewModel::removeRows ( int row,int count,const QModelIndex & /*parent*/ ) { beginRemoveRows(QModelIndex(),row,row + count - 1); endRemoveRows(); return true; } bool PeerViewModel::insertRows ( int row,int count,const QModelIndex & /*parent*/ ) { beginInsertRows(QModelIndex(),row,row + count - 1); endInsertRows(); return true; } bt::PeerInterface* PeerViewModel::indexToPeer(const QModelIndex & index) { if (!index.isValid() || index.row() >= items.count() || index.row() < 0) return 0; else return ((Item*)index.internalPointer())->peer; } class PeerViewModelItemCmp { public: PeerViewModelItemCmp(int col,Qt::SortOrder order) : col(col),order(order) {} bool operator()(PeerViewModel::Item* a,PeerViewModel::Item* b) { if (order == Qt::AscendingOrder) return a->lessThan(col,b); else return !a->lessThan(col,b); } int col; Qt::SortOrder order; }; void PeerViewModel::sort(int col, Qt::SortOrder order) { sort_column = col; sort_order = order; emit layoutAboutToBeChanged(); qStableSort(items.begin(),items.end(),PeerViewModelItemCmp(col,order)); emit layoutChanged(); } } diff --git a/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.h b/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.h index 539df207..d8d00113 100644 --- a/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.h +++ b/transfer-plugins/bittorrent/advanceddetails/peerviewmodel.h @@ -1,93 +1,94 @@ /*************************************************************************** * Copyright (C) 2008 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 KTPEERVIEWMODEL_H #define KTPEERVIEWMODEL_H +#include #include #include -#include + #include namespace kt { /** @author Joris Guisson Model for the PeerView */ class PeerViewModel : public QAbstractTableModel { Q_OBJECT public: PeerViewModel(QObject* parent); virtual ~PeerViewModel(); /// A peer has been added void peerAdded(bt::PeerInterface* peer); /// A peer has been removed void peerRemoved(bt::PeerInterface* peer); /** * Update the model */ void update(); void clear(); 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 bool removeRows(int row,int count,const QModelIndex & parent); virtual bool insertRows(int row,int count,const QModelIndex & parent); virtual QModelIndex index(int row,int column,const QModelIndex & parent = QModelIndex()) const; bt::PeerInterface* indexToPeer(const QModelIndex & idx); public slots: void sort(int col, Qt::SortOrder order); public: struct Item { bt::PeerInterface* peer; mutable bt::PeerInterface::Stats stats; QString country; QIcon flag; Item(bt::PeerInterface* peer); bool changed(int col,bool & modified) const; QVariant data(int col) const; QVariant decoration(int col) const; bool lessThan(int col,const Item* other) const; }; private: QList items; int sort_column; Qt::SortOrder sort_order; }; } #endif diff --git a/transfer-plugins/bittorrent/advanceddetails/torrentfilelistmodel.cpp b/transfer-plugins/bittorrent/advanceddetails/torrentfilelistmodel.cpp index 368548a4..42e5bd5b 100644 --- a/transfer-plugins/bittorrent/advanceddetails/torrentfilelistmodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/torrentfilelistmodel.cpp @@ -1,293 +1,295 @@ /*************************************************************************** * 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 "torrentfilelistmodel.h" -#include #include -#include #include + +#include +#include + #include #include #include using namespace bt; namespace kt { TorrentFileListModel::TorrentFileListModel(bt::TorrentInterface* tc,DeselectMode mode,QObject* parent) : TorrentFileModel(tc,mode,parent) { } TorrentFileListModel::~TorrentFileListModel() {} int TorrentFileListModel::rowCount(const QModelIndex & parent) const { if (!parent.isValid()) return tc->getStats().multi_file_torrent ? tc->getNumFiles() : 1; else return 0; } int TorrentFileListModel::columnCount(const QModelIndex & parent) const { if (!parent.isValid()) return 2; else return 0; } QVariant TorrentFileListModel::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 TorrentFileListModel::data(const QModelIndex & index, int role) const { if (!index.isValid()) return QVariant(); int r = index.row(); int nfiles = rowCount(QModelIndex()); bool multi = tc->getStats().multi_file_torrent; if (r < 0 || r >= nfiles) return QVariant(); const TorrentStats & s = tc->getStats(); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case 0: if (multi) return tc->getTorrentFile(r).getUserModifiedPath(); else return tc->getUserModifiedFileName(); case 1: if (multi) return BytesToString(tc->getTorrentFile(r).getSize()); else return BytesToString(s.total_bytes); default: return QVariant(); } } else if (role == Qt::UserRole) // sorting { switch (index.column()) { case 0: if (multi) return tc->getTorrentFile(r).getUserModifiedPath(); else return tc->getUserModifiedFileName(); case 1: if (multi) return tc->getTorrentFile(r).getSize(); else return s.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 (multi) return QIcon::fromTheme(KMimeType::findByPath(tc->getTorrentFile(r).getPath())->iconName()); else return QIcon::fromTheme(KMimeType::findByPath(s.torrent_name)->iconName()); } else if (role == Qt::CheckStateRole && index.column() == 0) { if (multi) return tc->getTorrentFile(r).doNotDownload() ? Qt::Unchecked : Qt::Checked;; } return QVariant(); } QModelIndex TorrentFileListModel::parent(const QModelIndex & index) const { Q_UNUSED(index) return QModelIndex(); } QModelIndex TorrentFileListModel::index(int row,int column,const QModelIndex & parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); else { bt::TorrentFileInterface* f = &tc->getTorrentFile(row); return createIndex(row,column,f); } } bool TorrentFileListModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (!index.isValid()) return false; if (role == Qt::CheckStateRole) { Qt::CheckState newState = static_cast(value.toInt()); bt::TorrentFileInterface & file = tc->getTorrentFile(index.row()); 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)); checkStateChanged(); return true; } else if (role == Qt::EditRole) { QString path = value.toString(); if (path.isEmpty()) return false; if (tc->getStats().multi_file_torrent) { bt::TorrentFileInterface & file = tc->getTorrentFile(index.row()); // keep track of modified paths file.setUserModifiedPath(path); } else { // change the name of the file or toplevel directory tc->setUserModifiedFileName(path); } dataChanged(createIndex(index.row(),0),createIndex(index.row(),columnCount(index) - 1)); return true; } return false; } void TorrentFileListModel::checkAll() { if (tc->getStats().multi_file_torrent) { for (Uint32 i = 0;i < tc->getNumFiles();++i) setData(index(i,0,QModelIndex()),Qt::Checked,Qt::CheckStateRole); } } void TorrentFileListModel::uncheckAll() { if (tc->getStats().multi_file_torrent) { for (Uint32 i = 0;i < tc->getNumFiles();++i) setData(index(i,0,QModelIndex()),Qt::Unchecked,Qt::CheckStateRole); } } void TorrentFileListModel::invertCheck() { if (!tc->getStats().multi_file_torrent) return; for (Uint32 i = 0;i < tc->getNumFiles();++i) invertCheck(index(i,0,QModelIndex())); } void TorrentFileListModel::invertCheck(const QModelIndex & idx) { if (tc->getTorrentFile(idx.row()).doNotDownload()) setData(idx,Qt::Checked,Qt::CheckStateRole); else setData(idx,Qt::Unchecked,Qt::CheckStateRole); } bt::Uint64 TorrentFileListModel::bytesToDownload() { if (tc->getStats().multi_file_torrent) { bt::Uint64 ret = 0; for (Uint32 i = 0;i < tc->getNumFiles();++i) { const bt::TorrentFileInterface & file = tc->getTorrentFile(i); if (!file.doNotDownload()) ret += file.getSize(); } return ret; } else return tc->getStats().total_bytes; } bt::TorrentFileInterface* TorrentFileListModel::indexToFile(const QModelIndex & idx) { if (!idx.isValid()) return 0; int r = idx.row(); if (r < 0 || r >= rowCount(QModelIndex())) return 0; else return &tc->getTorrentFile(r); } QString TorrentFileListModel::dirPath(const QModelIndex & idx) { if (!idx.isValid()) return QString(); int r = idx.row(); if (r < 0 || r >= rowCount(QModelIndex())) return QString(); else return tc->getTorrentFile(r).getPath(); } void TorrentFileListModel::changePriority(const QModelIndexList & indexes,bt::Priority newpriority) { foreach (const QModelIndex &idx,indexes) { setData(idx,newpriority,Qt::UserRole); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/torrentfilemodel.h b/transfer-plugins/bittorrent/advanceddetails/torrentfilemodel.h index 0d92e2fa..7890026a 100644 --- a/transfer-plugins/bittorrent/advanceddetails/torrentfilemodel.h +++ b/transfer-plugins/bittorrent/advanceddetails/torrentfilemodel.h @@ -1,147 +1,148 @@ /*************************************************************************** * 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 KTTORRENTFILEMODEL_HH #define KTTORRENTFILEMODEL_HH -#include #include +#include + #include class QTreeView; class QSortFilterProxyModel; namespace bt { class TorrentInterface; class TorrentFileInterface; } namespace kt { class TorrentFileModel : public QAbstractItemModel { Q_OBJECT public: enum DeselectMode { KEEP_FILES,DELETE_FILES }; TorrentFileModel(bt::TorrentInterface* tc,DeselectMode mode,QObject* parent); virtual ~TorrentFileModel(); /** * Check all the files in the torrent. */ virtual void checkAll() = 0; /** * Uncheck all files in the torrent. */ virtual void uncheckAll() = 0; /** * Invert the check of each file of the torrent */ virtual void invertCheck() = 0; /** * Calculate the number of bytes to download * @return Bytes to download */ virtual bt::Uint64 bytesToDownload() = 0; /** * Save which items are expanded. * @param pm Proxy model of the view * @param tv The QTreeView * @return The expanded state encoded in a byte array */ virtual QByteArray saveExpandedState(QSortFilterProxyModel* pm,QTreeView* tv); /** * Retore the expanded state of the tree.in a QTreeView * @param pm Proxy model of the view * @param tv The QTreeView * @param state The encoded expanded state */ virtual void loadExpandedState(QSortFilterProxyModel* pm,QTreeView* tv,const QByteArray & state); /** * Convert a model index to a file. * @param idx The model index * @return The file index or 0 for a directory **/ virtual bt::TorrentFileInterface* indexToFile(const QModelIndex & idx) = 0; /** * Get the path of a directory (root directory not included) * @param idx The model index * @return The path */ virtual QString dirPath(const QModelIndex & idx) = 0; /** * Change the priority of a bunch of items. * @param indexes The list of items * @param newpriority The new priority */ virtual void changePriority(const QModelIndexList & indexes,bt::Priority newpriority) = 0; /** * Missing files have been marked DND, update the preview and selection information. */ virtual void missingFilesMarkedDND(); /** * Update gui if necessary */ virtual void update(); /** * Codec has changed, so update the model. */ virtual void onCodecChange(); /// Set the file names editable void setFileNamesEditable(bool on) {file_names_editable = on;} /// Are the file names editable bool fileNamesEditable() const {return file_names_editable;} virtual Qt::ItemFlags flags(const QModelIndex & index) const; virtual void filePercentageChanged(bt::TorrentFileInterface* file,float percentage); virtual void filePreviewChanged(bt::TorrentFileInterface* file,bool preview); signals: /** * Emitted whenever one or more items changes check state */ void checkStateChanged(); protected: bt::TorrentInterface* tc; DeselectMode mode; bool file_names_editable; }; } #endif diff --git a/transfer-plugins/bittorrent/advanceddetails/torrentfiletreemodel.cpp b/transfer-plugins/bittorrent/advanceddetails/torrentfiletreemodel.cpp index 2e6ede5c..4ae3bc4f 100644 --- a/transfer-plugins/bittorrent/advanceddetails/torrentfiletreemodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/torrentfiletreemodel.cpp @@ -1,713 +1,715 @@ /*************************************************************************** * 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 "torrentfiletreemodel.h" -#include +#include +#include + #include -#include -#include #include +#include + #include #include #include #include #include #include #include using namespace bt; namespace kt { TorrentFileTreeModel::Node::Node(Node* parent,bt::TorrentFileInterface* file, const QString & name, const bt::Uint32 total_chunks) : parent(parent),file(file),name(name),size(0),chunks(total_chunks),chunks_set(false),percentage(0.0f) { chunks.setAll(false); } TorrentFileTreeModel::Node::Node(Node* parent,const QString & name, const bt::Uint32 total_chunks) : parent(parent),file(nullptr),name(name),size(0),chunks(total_chunks),chunks_set(false),percentage(0.0f) { chunks.setAll(false); } TorrentFileTreeModel::Node::~Node() { qDeleteAll(children); } void TorrentFileTreeModel::Node::insert(const QString & path,bt::TorrentFileInterface* file,bt::Uint32 num_chunks) { int p = path.indexOf(bt::DirSeparator()); if (p == -1) { // the file is part of this directory children.append(new Node(this,file,path,num_chunks)); } else { QString subdir = path.left(p); foreach (Node* n,children) { if (n->name == subdir) { n->insert(path.mid(p+1),file, num_chunks); return; } } Node* n = new Node(this,subdir,num_chunks); children.append(n); n->insert(path.mid(p+1),file, num_chunks); } } 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; } void TorrentFileTreeModel::Node::fillChunks() { if (chunks_set) return; if (!file) { foreach(Node* n, children) { n->fillChunks(); chunks.orBitSet(n->chunks); } } else { for (Uint32 i = file->getFirstChunk(); i <= file->getLastChunk(); ++i) chunks.set(i, true); } chunks_set = true; } void TorrentFileTreeModel::Node::updatePercentage(const BitSet & havechunks) { if (!chunks_set) fillChunks(); // make sure we know the chunks which are part of this node if (file) { percentage = file->getDownloadPercentage(); } else { if (havechunks.numOnBits() == 0 || chunks.numOnBits() == 0) { percentage = 0.0f; } else if (havechunks.allOn()) { percentage = 100.0f; } else { // take the chunks of the node and // logical and them with the chunks we have BitSet tmp(chunks); tmp.andBitSet(havechunks); percentage = 100.0f * ((float)tmp.numOnBits() / (float)chunks.numOnBits()); } } if (parent) parent->updatePercentage(havechunks); // update the percentage of the parent } void TorrentFileTreeModel::Node::initPercentage(const bt::TorrentInterface* tc,const bt::BitSet & havechunks) { if (!chunks_set) fillChunks(); if (!tc->getStats().multi_file_torrent) { percentage = bt::Percentage(tc->getStats()); return; } if (file) { percentage = file->getDownloadPercentage(); } else { if (havechunks.numOnBits() == 0 || chunks.numOnBits() == 0) { percentage = 0.0f; } else if (havechunks.allOn()) { percentage = 100.0f; } else { // take the chunks of the node and // logical and them with the chunks we have BitSet tmp(chunks); tmp.andBitSet(havechunks); percentage = 100.0f * ((float)tmp.numOnBits() / (float)chunks.numOnBits()); } foreach (Node* n,children) n->initPercentage(tc,havechunks); // update the percentage of the children } } 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,QSortFilterProxyModel* pm,QTreeView* tv,BEncoder* enc) { if (file) return; enc->write(QByteArray("expanded")); enc->write((Uint32)(tv->isExpanded(pm->mapFromSource(index)) ? 1 : 0)); int idx = 0; foreach (Node* n,children) { if (!n->file) { enc->write(n->name.toUtf8()); enc->beginDict(); n->saveExpandedState(index.child(idx,0),pm,tv,enc); enc->end(); } ++idx; } } void TorrentFileTreeModel::Node::loadExpandedState(const QModelIndex & index,QSortFilterProxyModel* pm,QTreeView* tv,BNode* n) { if (file) return; BDictNode* dict = dynamic_cast(n); if (!dict) return; BValueNode* v = dict->getValue(QByteArray("expanded")); if (v) tv->setExpanded(pm->mapFromSource(index),v->data().toInt() == 1); int idx = 0; foreach (Node* n,children) { if (!n->file) { BDictNode* d = dict->getDict(n->name.toUtf8()); if (d) n->loadExpandedState(index.child(idx,0),pm,tv,d); } idx++; } } QString TorrentFileTreeModel::Node::path() { if (!parent) return QString(); // the root node must not be included in the path if (file) return name; else return parent->path() + name + bt::DirSeparator(); } TorrentFileTreeModel::TorrentFileTreeModel(bt::TorrentInterface* tc,DeselectMode mode,QObject* parent) : TorrentFileModel(tc,mode,parent),root(nullptr),emit_check_state_change(true) { if (tc->getStats().multi_file_torrent) constructTree(); else root = new Node(nullptr,tc->getStats().torrent_name,tc->getStats().total_chunks); } TorrentFileTreeModel::~TorrentFileTreeModel() { delete root; } void TorrentFileTreeModel::constructTree() { bt::Uint32 num_chunks = tc->getStats().total_chunks; if (!root) root = new Node(nullptr,tc->getUserModifiedFileName(),num_chunks); for (Uint32 i = 0;i < tc->getNumFiles();++i) { bt::TorrentFileInterface & tf = tc->getTorrentFile(i); root->insert(tf.getUserModifiedPath(),&tf,num_chunks); } } void TorrentFileTreeModel::onCodecChange() { delete root; root = nullptr; constructTree(); reset(); } 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 || role == Qt::EditRole) { 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::UserRole) // sorting { switch (index.column()) { case 0: return n->name; case 1: if (tc->getStats().multi_file_torrent) return n->fileSize(tc); else return 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 ? QIcon::fromTheme("folder") : QIcon::fromTheme(KMimeType::findByPath(tc->getStats().torrent_name)->iconName()); else return QIcon::fromTheme(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 = nullptr; 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(); } } bool TorrentFileTreeModel::setCheckState(const QModelIndex & index, Qt::CheckState state) { Node* n = static_cast(index.internalPointer()); if (!n) return false; if (!n->file) { bool reenable = false; if (emit_check_state_change) { reenable = true; emit_check_state_change = false; } for (int i = 0;i < n->children.count();i++) { // recurse down the tree setCheckState(index.child(i,0),state); } if (reenable) emit_check_state_change = true; } else { bt::TorrentFileInterface* file = n->file; if (state == 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::modifyPathOfFiles(Node* n,const QString & path) { for (int i = 0;i < n->children.count();i++) { Node* c = n->children.at(i); if (!c->file) // another directory, continue recursively modifyPathOfFiles(c, path + c->name + bt::DirSeparator()); else c->file->setUserModifiedPath(path + c->name); } } bool TorrentFileTreeModel::setName(const QModelIndex & index,const QString & name) { Node* n = static_cast(index.internalPointer()); if (!n || name.isEmpty() || name.contains(bt::DirSeparator())) return false; if (!tc->getStats().multi_file_torrent) { // single file case so we only need to change the user modified name tc->setUserModifiedFileName(name); n->name = name; dataChanged(index,index); return true; } if (!n->file) { // we are in a directory n->name = name; if (!n->parent) { // toplevel directory name has changed tc->setUserModifiedFileName(name); } dataChanged(index,index); // modify the path of all files modifyPathOfFiles(n,n->path()); return true; } else { n->name = name; n->file->setUserModifiedPath(n->path()); dataChanged(index,index); return true; } } bool TorrentFileTreeModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (!index.isValid()) return false; if (role == Qt::CheckStateRole) return setCheckState(index, static_cast(value.toInt())); else if (role == Qt::EditRole) return setName(index,value.toString()); return false; } 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 (int 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; } QByteArray TorrentFileTreeModel::saveExpandedState(QSortFilterProxyModel* pm,QTreeView* tv) { if (!tc->getStats().multi_file_torrent) return QByteArray(); QByteArray data; BEncoder enc(new BEncoderBufferOutput(data)); enc.beginDict(); root->saveExpandedState(index(0,0,QModelIndex()),pm,tv,&enc); enc.end(); return data; } void TorrentFileTreeModel::loadExpandedState(QSortFilterProxyModel* pm,QTreeView* tv,const QByteArray & state) { if (!tc->getStats().multi_file_torrent) return; BDecoder dec(state,false,0); BNode* n = dec.decode(); if (n && n->getType() == BNode::DICT) { root->loadExpandedState(index(0,0,QModelIndex()),pm,tv,n); } delete n; } bt::TorrentFileInterface* TorrentFileTreeModel::indexToFile(const QModelIndex & idx) { if (!idx.isValid()) return 0; Node* n = (Node*)idx.internalPointer(); if (!n) return 0; return n->file; } QString TorrentFileTreeModel::dirPath(const QModelIndex & idx) { if (!idx.isValid()) return QString(); Node* n = (Node*)idx.internalPointer(); if (!n || n == root) return QString(); QString ret = n->name; do { n = n->parent; if (n && n->parent) ret = n->name + bt::DirSeparator() + ret; }while (n); return ret; } void TorrentFileTreeModel::changePriority(const QModelIndexList & indexes,bt::Priority newpriority) { foreach (const QModelIndex &idx,indexes) { Node* n = (Node*)idx.internalPointer(); if (!n) continue; setData(idx,newpriority,Qt::UserRole); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/trackermodel.cpp b/transfer-plugins/bittorrent/advanceddetails/trackermodel.cpp index 75163ef6..89569a47 100644 --- a/transfer-plugins/bittorrent/advanceddetails/trackermodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/trackermodel.cpp @@ -1,314 +1,317 @@ /*************************************************************************** * Copyright (C) 2008 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 "trackermodel.h" + #include -#include + +#include + #include #include namespace kt { TrackerModel::TrackerModel(QObject* parent) : QAbstractTableModel(parent),tc(nullptr) { running = false; } TrackerModel::~TrackerModel() { qDeleteAll(trackers); } void TrackerModel::changeTC(bt::TorrentInterface* tc) { qDeleteAll(trackers); trackers.clear(); this->tc = tc; if (tc) { QList tracker_list = tc->getTrackersList()->getTrackers(); foreach (bt::TrackerInterface* trk,tracker_list) { trackers.append(new Item(trk)); } } reset(); } void TrackerModel::update() { if (!tc) return; int idx = 0; foreach (Item* t,trackers) { if (t->update()) emit dataChanged(index(idx,1),index(idx,5)); idx++; } running = tc->getStats().running; } int TrackerModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !tc) return 0; else return trackers.count(); } int TrackerModel::columnCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; else return 6; } QVariant TrackerModel::data(const QModelIndex &index, int role) const { if (!tc || !index.isValid() || index.row() < 0 || index.row() >= trackers.count()) return QVariant(); Item* item = (Item*)index.internalPointer(); if (!item) return QVariant(); bt::TrackerInterface* trk = item->trk; if (role == Qt::CheckStateRole && index.column() == 0) { return trk->isEnabled() ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::DisplayRole) { return item->displayData(index.column()); } else if (role == Qt::UserRole) { return item->sortData(index.column()); } else if (role == Qt::ForegroundRole && index.column() == 1 && trk->trackerStatus() == bt::TRACKER_ERROR) { return QVariant::fromValue(Qt::red); } return QVariant(); } bool TrackerModel::setData(const QModelIndex & index,const QVariant & value,int role) { if (!tc || !index.isValid() || index.row() < 0 || index.row() >= trackers.count()) return false; if (role == Qt::CheckStateRole) { QUrl url = trackers.at(index.row())->trk->trackerURL(); tc->getTrackersList()->setTrackerEnabled(url,(Qt::CheckState)value.toUInt() == Qt::Checked); return true; } return false; } QVariant TrackerModel::headerData(int section, Qt::Orientation orientation,int role) const { if (orientation != Qt::Horizontal) return QVariant(); if (role == Qt::DisplayRole) { switch (section) { case 0: return i18n("Url"); case 1: return i18n("Status"); case 2: return i18n("Seeders"); case 3: return i18n("Leechers"); case 4: return i18n("Times Downloaded"); case 5: return i18n("Next Update"); } } return QVariant(); } bool TrackerModel::insertRows(int row,int count,const QModelIndex & parent) { Q_UNUSED(parent) beginInsertRows(QModelIndex(),row,row + count - 1); if (tc) { QList tracker_list = tc->getTrackersList()->getTrackers(); QList::iterator i = trackers.begin(); foreach (bt::TrackerInterface* trk,tracker_list) { if (i != trackers.end()) { Item* item = *i; item->trk = trk; } else trackers.append(new Item(trk)); i++; } } endInsertRows(); return true; } bool TrackerModel::removeRows(int row,int count,const QModelIndex & parent) { Q_UNUSED(parent) beginRemoveRows(QModelIndex(),row,row + count - 1); if (tc) { for (int i = 0;i < count;i++) { Item* item = trackers.takeAt(row); QUrl url = item->trk->trackerURL(); tc->getTrackersList()->removeTracker(url); delete item; } } endRemoveRows(); return true; } Qt::ItemFlags TrackerModel::flags(const QModelIndex & index) const { if (!tc || !index.isValid() || index.row() >= trackers.count() || index.row() < 0 || index.column() != 0) return QAbstractItemModel::flags(index); else return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; } QModelIndex TrackerModel::index(int row, int column, const QModelIndex& parent) const { if (parent.isValid() || row < 0 || row >= trackers.count() || column < 0 || column >= 6) return QModelIndex(); else return createIndex(row,column,trackers.at(row)); } QUrl TrackerModel::trackerUrl(const QModelIndex & index) { if (!tc || !index.isValid() || index.row() < 0 || index.row() >= trackers.count()) return QUrl(); return ((Item*)index.internalPointer())->trk->trackerURL(); } bt::TrackerInterface* TrackerModel::tracker(const QModelIndex & index) { if (!tc || !index.isValid() || index.row() < 0 || index.row() >= trackers.count()) return 0; return ((Item*)index.internalPointer())->trk; } ////////////////////////////////////////// TrackerModel::Item::Item(bt::TrackerInterface* tracker) : trk(tracker) { seeders = leechers = -1; times_downloaded = -1; time_to_next_update = 0; status = tracker->trackerStatus(); } bool TrackerModel::Item::update() { bool ret = false; if (status != trk->trackerStatus()) { status = trk->trackerStatus(); ret = true; } if (seeders != trk->getNumSeeders()) { seeders = trk->getNumSeeders(); ret = true; } if (leechers != trk->getNumLeechers()) { leechers = trk->getNumLeechers(); ret = true; } if (times_downloaded != trk->getTotalTimesDownloaded()) { times_downloaded = trk->getTotalTimesDownloaded(); ret = true; } if (static_cast(time_to_next_update) != trk->timeToNextUpdate()) { time_to_next_update = trk->timeToNextUpdate(); ret = true; } return ret; } QVariant TrackerModel::Item::displayData(int column) const { switch (column) { case 0: return trk->trackerURL().toDisplayString(); case 1: return trk->trackerStatusString(); case 2: return seeders >= 0 ? seeders : QVariant(); case 3: return leechers >= 0 ? leechers : QVariant(); case 4: return times_downloaded >= 0 ? times_downloaded : QVariant(); case 5: { int secs = time_to_next_update; if (secs) return QTime().addSecs(secs).toString("mm:ss"); else return QVariant(); } default: return QVariant(); } } QVariant TrackerModel::Item::sortData(int column) const { switch (column) { case 0: return trk->trackerURL().toDisplayString(); case 1: return status; case 2: return seeders; case 3: return leechers; case 4: return times_downloaded; case 5: return time_to_next_update; default: return QVariant(); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/trackermodel.h b/transfer-plugins/bittorrent/advanceddetails/trackermodel.h index 74ae99e2..d68c3de0 100644 --- a/transfer-plugins/bittorrent/advanceddetails/trackermodel.h +++ b/transfer-plugins/bittorrent/advanceddetails/trackermodel.h @@ -1,89 +1,90 @@ /*************************************************************************** * Copyright (C) 2008 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 KTTRACKERMODEL_H #define KTTRACKERMODEL_H +#include #include #include -#include + #include namespace bt { class TorrentInterface; } namespace kt { /** @author */ class TrackerModel : public QAbstractTableModel { Q_OBJECT public: TrackerModel(QObject* parent); virtual ~TrackerModel(); void changeTC(bt::TorrentInterface* tc); void update(); virtual int rowCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex & index,const QVariant & value,int role); virtual QVariant headerData(int section, Qt::Orientation orientation,int role) const; virtual bool insertRows(int row,int count,const QModelIndex & parent); virtual bool removeRows(int row,int count,const QModelIndex & parent); virtual Qt::ItemFlags flags(const QModelIndex & index) const; virtual QModelIndex index(int row,int column,const QModelIndex & parent = QModelIndex()) const; /// Get a tracker url given a model index QUrl trackerUrl(const QModelIndex & idx); /// Get a tracker given a model index bt::TrackerInterface* tracker(const QModelIndex & idx); private: struct Item { bt::TrackerInterface* trk; bt::TrackerStatus status; int seeders; int leechers; int times_downloaded; int time_to_next_update; Item(bt::TrackerInterface* tracker); bool update(); QVariant displayData(int column) const; QVariant sortData(int column) const; }; bt::TorrentInterface* tc; QList trackers; bool running; }; } #endif diff --git a/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp b/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp index 59f3647e..cd935871 100644 --- a/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp @@ -1,231 +1,232 @@ /*************************************************************************** * Copyright (C) 2006-2007 by Joris Guisson, 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 "trackerview.h" #include #include -#include -#include -#include -#include -#include -#include + +#include +#include +#include +#include +#include + #include #include #include #include #include #include "trackermodel.h" using namespace bt; namespace kt { TrackerView::TrackerView(QWidget *parent) : QWidget(parent), tc(nullptr) { setupUi(this); model = new TrackerModel(this); proxy_model = new QSortFilterProxyModel(this); proxy_model->setSortRole(Qt::UserRole); proxy_model->setSourceModel(model); m_tracker_list->setModel(proxy_model); m_tracker_list->setAllColumnsShowFocus(true); m_tracker_list->setRootIsDecorated(false); m_tracker_list->setAlternatingRowColors(true); m_tracker_list->setSortingEnabled(true); connect(m_add_tracker,SIGNAL(clicked()),this,SLOT(addClicked())); connect(m_remove_tracker,SIGNAL(clicked()),this,SLOT(removeClicked())); connect(m_change_tracker,SIGNAL(clicked()),this,SLOT(changeClicked())); connect(m_restore_defaults,SIGNAL(clicked()),this,SLOT(restoreClicked())); connect(m_tracker_list->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), this,SLOT(currentChanged(QModelIndex,QModelIndex))); connect(m_scrape,SIGNAL(clicked()),this,SLOT(scrapeClicked())); m_add_tracker->setIcon(QIcon::fromTheme("list-add")); m_remove_tracker->setIcon(QIcon::fromTheme("list-remove")); m_restore_defaults->setIcon(QIcon::fromTheme("kt-restore-defaults")); m_change_tracker->setIcon(QIcon::fromTheme("kt-change-tracker")); setEnabled(false); torrentChanged(nullptr); } TrackerView::~TrackerView() { } void TrackerView::addClicked() { if (!tc) return; bool ok = false; QClipboard* clipboard = QApplication::clipboard(); QString text = KInputDialog::getText( i18n("Add Tracker"),i18n("Enter the URL of the tracker:"),clipboard->text(),&ok,this); if (!ok) return; KUrl url(text); if (!url.isValid()) { KMessageBox::error(nullptr, i18n("Malformed URL.")); return; } // check for dupes if (!tc->getTrackersList()->addTracker(url,true)) { KMessageBox::sorry(nullptr,i18n("There already is a tracker named %1.",text)); } else { model->insertRow(model->rowCount(QModelIndex())); } } void TrackerView::removeClicked() { QModelIndex current = proxy_model->mapToSource(m_tracker_list->selectionModel()->currentIndex()); if (!current.isValid()) return; model->removeRow(current.row()); } void TrackerView::changeClicked() { QModelIndex current = m_tracker_list->selectionModel()->currentIndex(); if (!current.isValid()) return; bt::TrackersList* tlist = tc->getTrackersList(); bt::TrackerInterface* trk = model->tracker(proxy_model->mapToSource(current)); if (trk && trk->isEnabled()) tlist->setCurrentTracker(trk); } void TrackerView::restoreClicked() { tc->getTrackersList()->restoreDefault(); tc->updateTracker(); model->changeTC(tc); // trigger reset } void TrackerView::updateClicked() { if(!tc) return; tc->updateTracker(); } void TrackerView::scrapeClicked() { if(!tc) return; tc->scrapeTracker(); } void TrackerView::changeTC(TorrentInterface* ti) { if (tc == ti) return; setEnabled(ti != nullptr); torrentChanged(ti); update(); } void TrackerView::update() { if (tc) model->update(); } void TrackerView::torrentChanged(TorrentInterface* ti) { tc = ti; if(!tc) { m_add_tracker->setEnabled(false); m_remove_tracker->setEnabled(false); m_restore_defaults->setEnabled(false); m_change_tracker->setEnabled(false); m_scrape->setEnabled(false); model->changeTC(nullptr); } else { m_add_tracker->setEnabled(true); m_remove_tracker->setEnabled(true); m_restore_defaults->setEnabled(true); m_scrape->setEnabled(true); model->changeTC(tc); currentChanged(m_tracker_list->selectionModel()->currentIndex(),QModelIndex()); } } void TrackerView::currentChanged(const QModelIndex & current,const QModelIndex & previous) { Q_UNUSED(previous) if (!tc) { m_change_tracker->setEnabled(false); m_remove_tracker->setEnabled(false); return; } const TorrentStats & s = tc->getStats(); bt::TrackerInterface* trk = model->tracker(proxy_model->mapToSource(current)); bool enabled = trk ? trk->isEnabled() : false; m_change_tracker->setEnabled(s.running && model->rowCount(QModelIndex()) > 1 && enabled); m_remove_tracker->setEnabled(trk && tc->getTrackersList()->canRemoveTracker(trk)); } void TrackerView::saveState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("TrackerView"); QByteArray s = m_tracker_list->header()->saveState(); g.writeEntry("state",s.toBase64()); } void TrackerView::loadState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("TrackerView"); QByteArray s = QByteArray::fromBase64(g.readEntry("state",QByteArray())); if (!s.isNull()) { QHeaderView* v = m_tracker_list->header(); v->restoreState(s); } } } diff --git a/transfer-plugins/bittorrent/advanceddetails/webseedsmodel.cpp b/transfer-plugins/bittorrent/advanceddetails/webseedsmodel.cpp index 76b94493..72c44ad4 100644 --- a/transfer-plugins/bittorrent/advanceddetails/webseedsmodel.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/webseedsmodel.cpp @@ -1,154 +1,156 @@ /*************************************************************************** * Copyright (C) 2008 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 "webseedsmodel.h" -#include + +#include + #include #include #include using namespace bt; namespace kt { WebSeedsModel::WebSeedsModel(QObject* parent) : QAbstractTableModel(parent),curr_tc(nullptr) { } WebSeedsModel::~WebSeedsModel() { } void WebSeedsModel::changeTC(bt::TorrentInterface* tc) { curr_tc = tc; items.clear(); if (tc) { for (Uint32 i = 0;i < tc->getNumWebSeeds();++i) { const bt::WebSeedInterface* ws = curr_tc->getWebSeed(i); Item item; item.status = ws->getStatus(); item.downloaded = ws->getTotalDownloaded(); item.speed = ws->getDownloadRate(); items.append(item); } } reset(); } bool WebSeedsModel::update() { if (!curr_tc) return false; bool ret = false; for (Uint32 i = 0;i < curr_tc->getNumWebSeeds();++i) { const bt::WebSeedInterface* ws = curr_tc->getWebSeed(i); Item & item = items[i]; bool changed = false; if (item.status != ws->getStatus()) { changed = true; item.status = ws->getStatus(); } if (item.downloaded != ws->getTotalDownloaded()) { changed = true; item.downloaded = ws->getTotalDownloaded(); } if (item.speed != ws->getDownloadRate()) { changed = true; item.speed = ws->getDownloadRate(); } if (changed) { dataChanged(createIndex(i,1),createIndex(i,3)); ret = true; } } return ret; } int WebSeedsModel::rowCount(const QModelIndex & parent) const { if (parent.isValid()) return 0; else return curr_tc ? curr_tc->getNumWebSeeds() : 0; } int WebSeedsModel::columnCount(const QModelIndex & parent) const { if (parent.isValid()) return 0; else return 4; } QVariant WebSeedsModel::headerData(int section, Qt::Orientation orientation,int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch (section) { case 0: return i18n("URL"); case 1: return i18n("Speed"); case 2: return i18n("Downloaded"); case 3: return i18n("Status"); default: return QVariant(); } } QVariant WebSeedsModel::data(const QModelIndex & index, int role) const { if (!curr_tc) return QVariant(); if (!index.isValid() || index.row() >= static_cast(curr_tc->getNumWebSeeds()) || index.row() < 0) return QVariant(); if (role == Qt::DisplayRole) { const bt::WebSeedInterface* ws = curr_tc->getWebSeed(index.row()); switch (index.column()) { case 0: return ws->getUrl().toDisplayString(); case 1: return bt::BytesPerSecToString(ws->getDownloadRate()); case 2: return bt::BytesToString(ws->getTotalDownloaded()); case 3: return ws->getStatus(); } } return QVariant(); } } diff --git a/transfer-plugins/bittorrent/advanceddetails/webseedstab.cpp b/transfer-plugins/bittorrent/advanceddetails/webseedstab.cpp index 015ceed5..29e878db 100644 --- a/transfer-plugins/bittorrent/advanceddetails/webseedstab.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/webseedstab.cpp @@ -1,167 +1,170 @@ /*************************************************************************** * Copyright (C) 2008 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 "webseedstab.h" + #include -#include -#include + +#include +#include + #include #include #include "webseedsmodel.h" using namespace bt; namespace kt { WebSeedsTab::WebSeedsTab(QWidget* parent) : QWidget(parent),curr_tc(nullptr) { setupUi(this); connect(m_add,SIGNAL(clicked()),this,SLOT(addWebSeed())); connect(m_remove,SIGNAL(clicked()),this,SLOT(removeWebSeed())); m_add->setIcon(QIcon::fromTheme("list-add")); m_remove->setIcon(QIcon::fromTheme("list-remove")); m_add->setEnabled(false); m_remove->setEnabled(false); m_webseed_list->setEnabled(false); model = new WebSeedsModel(this); proxy_model = new QSortFilterProxyModel(this); proxy_model->setSourceModel(model); proxy_model->setSortRole(Qt::UserRole); m_webseed_list->setModel(proxy_model); m_webseed_list->setSortingEnabled(true); connect(m_webseed_list->selectionModel(),SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this,SLOT(selectionChanged(QItemSelection,QItemSelection))); connect(m_webseed,SIGNAL(textChanged(QString)),this,SLOT(onWebSeedTextChanged(QString))); } WebSeedsTab::~WebSeedsTab() { } void WebSeedsTab::changeTC(bt::TorrentInterface* tc) { curr_tc = tc; model->changeTC(tc); m_add->setEnabled(curr_tc != nullptr); m_remove->setEnabled(curr_tc != nullptr); m_webseed_list->setEnabled(curr_tc != nullptr); m_webseed->setEnabled(curr_tc != nullptr); onWebSeedTextChanged(m_webseed->text()); // see if we need to enable or disable the remove button if (curr_tc) selectionChanged(m_webseed_list->selectionModel()->selectedRows()); } void WebSeedsTab::addWebSeed() { if (!curr_tc) return; QUrl url(m_webseed->text()); if (curr_tc != 0 && url.isValid() && url.scheme() == "http") { if (curr_tc->addWebSeed(url)) { model->changeTC(curr_tc); m_webseed->clear(); } else { KMessageBox::error(this,i18n("Cannot add the webseed %1, it is already part of the list of webseeds.", url.toDisplayString())); } } } void WebSeedsTab::removeWebSeed() { if (!curr_tc) return; QModelIndexList idx_list = m_webseed_list->selectionModel()->selectedRows(); foreach (const QModelIndex &idx, idx_list) { const WebSeedInterface* ws = curr_tc->getWebSeed(proxy_model->mapToSource(idx).row()); if (ws && ws->isUserCreated()) { if (!curr_tc->removeWebSeed(ws->getUrl())) KMessageBox::error(this,i18n("Cannot remove webseed %1, it is part of the torrent.", ws->getUrl().toDisplayString())); } } model->changeTC(curr_tc); } void WebSeedsTab::selectionChanged(const QModelIndexList & indexes) { foreach (const QModelIndex &idx, indexes) { const WebSeedInterface* ws = curr_tc->getWebSeed(proxy_model->mapToSource(idx).row()); if (ws && ws->isUserCreated()) { m_remove->setEnabled(true); return; } } m_remove->setEnabled(false); } void WebSeedsTab::selectionChanged(const QItemSelection & selected, const QItemSelection & deselected) { Q_UNUSED(deselected) if (!curr_tc) return; selectionChanged(selected.indexes()); } void WebSeedsTab::onWebSeedTextChanged(const QString & ws) { QUrl url(ws); m_add->setEnabled(curr_tc != nullptr && url.isValid() && url.scheme() == "http"); } void WebSeedsTab::update() { if (model->update()) proxy_model->invalidate(); } void WebSeedsTab::saveState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("WebSeedsTab"); QByteArray s = m_webseed_list->header()->saveState(); g.writeEntry("state",s.toBase64()); } void WebSeedsTab::loadState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("WebSeedsTab"); QByteArray s = QByteArray::fromBase64(g.readEntry("state",QByteArray())); if (!s.isNull()) m_webseed_list->header()->restoreState(s); } } diff --git a/transfer-plugins/bittorrent/advanceddetails/webseedstab.h b/transfer-plugins/bittorrent/advanceddetails/webseedstab.h index 02542fd9..a667d7e0 100644 --- a/transfer-plugins/bittorrent/advanceddetails/webseedstab.h +++ b/transfer-plugins/bittorrent/advanceddetails/webseedstab.h @@ -1,77 +1,79 @@ /*************************************************************************** * Copyright (C) 2008 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 KTWEBSEEDSTAB_H #define KTWEBSEEDSTAB_H -#include #include +#include + #include + #include "ui_webseedstab.h" namespace bt { class TorrentInterface; } namespace kt { class WebSeedsModel; /** Tab which displays the list of webseeds of a torrent, and allows you to add or remove them. */ class WebSeedsTab : public QWidget,public Ui_WebSeedsTab { Q_OBJECT public: WebSeedsTab(QWidget* parent); virtual ~WebSeedsTab(); /** * Switch to a different torrent. * @param tc The torrent */ void changeTC(bt::TorrentInterface* tc); /// Check to see if the GUI needs to be updated void update(); void saveState(KSharedConfigPtr cfg); void loadState(KSharedConfigPtr cfg); private slots: void addWebSeed(); void removeWebSeed(); void onWebSeedTextChanged(const QString & ws); void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected); private: void selectionChanged(const QModelIndexList & indexes); private: bt::TorrentInterface* curr_tc; WebSeedsModel* model; QSortFilterProxyModel* proxy_model; }; } #endif diff --git a/transfer-plugins/bittorrent/btcache.cpp b/transfer-plugins/bittorrent/btcache.cpp index 333cc7ef..f9bed509 100644 --- a/transfer-plugins/bittorrent/btcache.cpp +++ b/transfer-plugins/bittorrent/btcache.cpp @@ -1,78 +1,77 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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. */ #include "btcache.h" #include -#include #include using namespace bt; //TODO: Support buffered mode? BTCache::BTCache(Torrent & tor,const QString & tmpdir,const QString & datadir) : Cache(tor, tmpdir, datadir), QObject(nullptr) { } BTCache::~BTCache() { } void BTCache::load(Chunk* c) { c->setData(0, Chunk::MMAPPED); } void BTCache::save(Chunk* c) { /*if (c->getStatus() == Chunk::MMAPPED) { KIO::fileoffset_t off = c->getIndex() * tor.getChunkSize(); qCDebug(KGET_DEBUG) << "Fileoffset is: " + QString::number(off); QByteArray data; QDataStream s(&data, QIODevice::WriteOnly | QIODevice::Unbuffered); s << c->getData(); emit dataArrived(off, data); c->clear(); c->setStatus(Chunk::ON_DISK); } else if (c->getStatus() == Chunk::BUFFERED) {*/ KIO::fileoffset_t off = c->getIndex() * tor.getChunkSize(); qCDebug(KGET_DEBUG) << "Fileoffset is: " + QString::number(off); QByteArray data; QDataStream s(&data, QIODevice::WriteOnly | QIODevice::Unbuffered); s << c->getData(); emit dataArrived(off, data); //fd->write(c->getData(),c->getSize(),off);//Send a signal here that the signal has arrived c->clear(); c->setStatus(Chunk::ON_DISK); //} } bool BTCache::prep(Chunk* c) { c->setData(0, Chunk::MMAPPED); return true; } void BTCache::deleteDataFiles() { } Cache* BTCacheFactory::create(Torrent & tor,const QString & tmpdir,const QString & datadir) { BTCache *newcache = new BTCache(tor, tmpdir, datadir); emit cacheAdded(newcache); return newcache; } diff --git a/transfer-plugins/bittorrent/btcache.h b/transfer-plugins/bittorrent/btcache.h index 37cae6d7..b8d5f165 100644 --- a/transfer-plugins/bittorrent/btcache.h +++ b/transfer-plugins/bittorrent/btcache.h @@ -1,169 +1,170 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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. */ #ifndef KGETBTCACHE_H #define KGETBTCACHE_H #include #include -#include -#include +#include + #include +#include class QStringList; class KJob; namespace bt { class Torrent; class TorrentFile; class Chunk; class PreallocationThread; } using namespace bt; class BTCache : public QObject, public bt::Cache { Q_OBJECT public: BTCache(bt::Torrent & tor,const QString & tmpdir,const QString & datadir); ~BTCache(); /** * Load the file map of a torrent. * If it doesn't exist, it needs to be created. */ virtual void loadFileMap() {} /** * Save the file map of a torrent */ virtual void saveFileMap() {} /** * Get the actual output path. * @return The output path */ virtual QString getOutputPath() const {return QString();} /** * Changes the tmp dir. All data files should already been moved. * This just modifies the tmpdir variable. * @param ndir The new tmpdir */ virtual void changeTmpDir(const QString & ndir) {Q_UNUSED(ndir)} /** * Changes output path. All data files should already been moved. * This just modifies the datadir variable. * @param outputpath New output path */ virtual void changeOutputPath(const QString & outputpath) {Q_UNUSED(outputpath)} /** * Move the data files to a new directory. * @param ndir The directory * @return The job doing the move */ virtual KJob* moveDataFiles(const QString & ndir) {return nullptr;} /** * A move of a bunch of data files has finished * @param job The job doing the move */ virtual void moveDataFilesFinished(KJob* job) {Q_UNUSED(job)} /** * Load a chunk into memory. If something goes wrong, * an Error should be thrown. * @param c The Chunk */ virtual void load(Chunk* c); /** * Save a chunk to disk. If something goes wrong, * an Error should be thrown. * @param c The Chunk */ virtual void save(Chunk* c); /** * Prepare a chunk for downloading. * @param c The Chunk * @return true if ok, false otherwise */ virtual bool prep(Chunk* c); /** * Create all the data files to store the data. */ virtual void create() {} /** * Close the cache file(s). */ virtual void close() {} /** * Open the cache file(s) */ virtual void open() {} /// Does nothing, can be overridden to be alerted of download status changes of a TorrentFile virtual void downloadStatusChanged(TorrentFile*, bool) {} /** * Preallocate diskspace for all files * @param prealloc The thread doing the preallocation */ virtual void preallocateDiskSpace(PreallocationThread* prealloc) {Q_UNUSED(prealloc)} /** * Test all files and see if they are not missing. * If so put them in a list */ virtual bool hasMissingFiles(QStringList & sl) {return false;} //We never have missing files, cause we don't have files :P /** * Delete all data files, in case of multi file torrents * empty directories should also be deleted. */ virtual KJob* deleteDataFiles() {return nullptr;}//TODO: Implement!!! virtual bt::PieceData* loadPiece(bt::Chunk*, bt::Uint32, bt::Uint32) {return nullptr;} virtual bt::PieceData* preparePiece(bt::Chunk*, bt::Uint32, bt::Uint32) {return nullptr;} virtual void savePiece(bt::PieceData*) {} /** * Get the number of bytes all the files of this torrent are currently using on disk. * */ virtual Uint64 diskUsage() {return 0;};//We always use 0 Bytes on HDD, cause we don't write to HDD signals: void dataArrived(const KIO::fileoffset_t &offset, const QByteArray &data); private: Torrent *m_tor; }; class BTCacheFactory : public QObject, public CacheFactory { Q_OBJECT public: BTCacheFactory() {} ~BTCacheFactory() {} virtual Cache* create(Torrent & tor,const QString & tmpdir,const QString & datadir); signals: void cacheAdded(BTCache* cache); }; #endif diff --git a/transfer-plugins/bittorrent/btchunkselector.cpp b/transfer-plugins/bittorrent/btchunkselector.cpp index bc3e4cd4..0c12e1a3 100644 --- a/transfer-plugins/bittorrent/btchunkselector.cpp +++ b/transfer-plugins/bittorrent/btchunkselector.cpp @@ -1,301 +1,302 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans Copyright (C) 2005 Joris Guisson 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. */ #include "btchunkselector.h" -#include -#include #include +#include +#include + #include #include #include #include #include #include #include #include using namespace bt; struct RareCmp { ChunkManager & cman; ChunkCounter & cc; bool warmup; RareCmp(ChunkManager & cman,ChunkCounter & cc,bool warmup) : cman(cman),cc(cc),warmup(warmup) {} bool operator()(Uint32 a,Uint32 b) { if (a >= cman.getNumChunks() || b >= cman.getNumChunks()) return false; // the sorting is done on two criteria, priority and rareness Priority pa = cman.getChunk(a)->getPriority(); Priority pb = cman.getChunk(b)->getPriority(); if (pa == pb) return normalCmp(a,b); // if both have same priority compare on rareness else if (pa > pb) // pa has priority over pb, so select pa return true; else // pb has priority over pa, so select pb return false; } bool normalCmp(Uint32 a,Uint32 b) { // during warmup mode choose most common chunks if (!warmup) return cc.get(a) < cc.get(b); else return cc.get(a) > cc.get(b); } }; BTChunkSelector::BTChunkSelector(ChunkManager & cman,Downloader & downer,PeerManager & pman) : ChunkSelectorInterface(cman,downer,pman) { std::vector tmp; for (Uint32 i = 0;i < cman.getNumChunks();++i) { if (!cman.getBitSet().get(i)) { tmp.push_back(i); } } std::random_shuffle(tmp.begin(),tmp.end()); // std::list does not support random_shuffle so we use a vector as a temporary storage // for the random_shuffle chunks.insert(chunks.begin(),tmp.begin(),tmp.end()); sort_timer.update(); } BTChunkSelector::~BTChunkSelector() { } Uint32 BTChunkSelector::leastPeers(const std::list & lp) { Uint32 sel = lp.front(); Uint32 cnt = downer.numDownloadersForChunk(sel); std::list::const_iterator itEnd = lp.end(); for (std::list::const_iterator i = lp.begin();i != itEnd;++i) { Uint32 cnt_i = downer.numDownloadersForChunk(*i); if (cnt_i < cnt) { sel = *i; cnt = cnt_i; } } return sel; } bool BTChunkSelector::select(PieceDownloader* pd,Uint32 & chunk) { const BitSet & bs = cman.getBitSet(); std::list preview; std::list normal; std::list first; Uint32 sel = cman.getNumChunks() + 1; // sort the chunks every 2 seconds if (sort_timer.getElapsedSinceUpdate() > 2000) { bool warmup = cman.getNumChunks() - cman.chunksLeft() <= 4; chunks.sort(RareCmp(cman,pman.getChunkCounter(),warmup)); sort_timer.update(); } std::list::iterator itr; std::list::iterator itrEnd = chunks.end(); for (itr = chunks.begin(); itr != itrEnd; ) { const Uint32 i = *itr; Chunk* c = cman.getChunk(i); // if we have the chunk remove it from the list if (bs.get(i)) { itr = chunks.erase(itr); } else { // pd has to have the selected chunk and it needs to be not excluded if (pd->hasChunk(i) && !c->isExcluded() && !c->isExcludedForDownloading()) { if (!downer.areWeDownloading(i)) { // we have a chunk sel = i; break; } switch (cman.getChunk(i)->getPriority()) { case PREVIEW_PRIORITY: preview.push_back(i); break; case FIRST_PRIORITY: first.push_back(i); break; case NORMAL_PRIORITY: normal.push_back(i); break; default: break; } } ++itr; } } if (sel >= cman.getNumChunks()) return false; // we have found one, now try to see if we cannot assign this PieceDownloader to a higher priority chunk switch (cman.getChunk(sel)->getPriority()) { case PREVIEW_PRIORITY: chunk = sel; return true; case FIRST_PRIORITY: if (preview.size() > 0) { chunk = leastPeers(preview); return true; } else { chunk = sel; return true; } break; case NORMAL_PRIORITY: if (preview.size() > 0) { chunk = leastPeers(preview); return true; } else if (first.size() > 0) { chunk = leastPeers(first); return true; } else { chunk = sel; return true; } break; case LAST_PRIORITY: if (preview.size() > 0) { chunk = leastPeers(preview); return true; } else if (first.size() > 0) { chunk = leastPeers(first); return true; } else if (normal.size() > 0) { chunk = leastPeers(normal); return true; } else { chunk = sel; return true; } break; default: chunk = sel; return true; } return false; } void BTChunkSelector::dataChecked(const BitSet & ok_chunks) { for (Uint32 i = 0;i < ok_chunks.getNumBits();++i) { bool in_chunks = std::find(chunks.begin(),chunks.end(),i) != chunks.end(); if (in_chunks && ok_chunks.get(i)) { // if we have the chunk, remove it from the chunks list chunks.remove(i); } else if (!in_chunks && !ok_chunks.get(i)) { // if we don't have the chunk, add it to the list if it wasn't allrready in there chunks.push_back(i); } } } void BTChunkSelector::reincluded(Uint32 from, Uint32 to) { // lets do a safety check first if (from >= cman.getNumChunks() || to >= cman.getNumChunks()) { Out(SYS_DIO|LOG_NOTICE) << "Internal error in chunkselector" << endl; return; } for (Uint32 i = from;i <= to;++i) { bool in_chunks = std::find(chunks.begin(),chunks.end(),i) != chunks.end(); if (!in_chunks && cman.getChunk(i)->getStatus() != Chunk::ON_DISK) { // Out(SYS_DIO|LOG_DEBUG) << "BTChunkSelector::reIncluded " << i << endl; chunks.push_back(i); } } } void BTChunkSelector::reinsert(Uint32 chunk) { bool in_chunks = std::find(chunks.begin(),chunks.end(),chunk) != chunks.end(); if (!in_chunks) chunks.push_back(chunk); } void BTChunkSelector::excludeAll() { chunks.clear(); } void BTChunkSelector::exclude(Uint32 chunk) { bool in_chunks = std::find(chunks.begin(),chunks.end(),chunk) != chunks.end(); if (in_chunks) chunks.remove(chunk); } BTChunkSelectorFactory::BTChunkSelectorFactory() { } BTChunkSelectorFactory::~BTChunkSelectorFactory() { } bt::ChunkSelectorInterface* BTChunkSelectorFactory::createChunkSelector(bt::ChunkManager & cman, bt::Downloader & downer, bt::PeerManager & pman) { BTChunkSelector *selector = new BTChunkSelector(cman, downer, pman); emit selectorAdded(selector); return selector; } diff --git a/transfer-plugins/bittorrent/btchunkselector.h b/transfer-plugins/bittorrent/btchunkselector.h index 8a82682e..a0f90b7d 100644 --- a/transfer-plugins/bittorrent/btchunkselector.h +++ b/transfer-plugins/bittorrent/btchunkselector.h @@ -1,62 +1,63 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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. */ #ifndef KGETBTCHUNKSELECTOR_H #define KGETBTCHUNKSELECTOR_H #include #include #include #include + #include namespace bt { class BitSet; class ChunkManager; class Downloader; class PeerManager; class PieceDownloader; } class BTChunkSelector : public bt::ChunkSelectorInterface { public: BTChunkSelector(bt::ChunkManager & cman,bt::Downloader & downer,bt::PeerManager & pman); ~BTChunkSelector(); virtual bool select(bt::PieceDownloader* pd,bt::Uint32 & chunk); virtual void dataChecked(const bt::BitSet & ok_chunks); virtual void reincluded(bt::Uint32 from, bt::Uint32 to); virtual void reinsert(bt::Uint32 chunk); virtual void excludeAll(); virtual void exclude(bt::Uint32 chunk); private: bt::Uint32 leastPeers(const std::list & lp); std::list chunks; bt::Timer sort_timer; }; class BTChunkSelectorFactory : public QObject, public bt::ChunkSelectorFactoryInterface { Q_OBJECT public: BTChunkSelectorFactory(); ~BTChunkSelectorFactory(); bt::ChunkSelectorInterface* createChunkSelector(bt::ChunkManager & cman, bt::Downloader & downer, bt::PeerManager & pman); signals: void selectorAdded(BTChunkSelector *selector); }; #endif diff --git a/transfer-plugins/bittorrent/btdatasource.cpp b/transfer-plugins/bittorrent/btdatasource.cpp index 88bcaa67..53bfffea 100644 --- a/transfer-plugins/bittorrent/btdatasource.cpp +++ b/transfer-plugins/bittorrent/btdatasource.cpp @@ -1,178 +1,177 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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. */ #include "btdatasource.h" #include "btcache.h" #include "btchunkselector.h" #include "bittorrentsettings.h" #include "core/download.h" #include #include #include #include #include #include #include #include #include -#include -#include +#include using namespace bt; BTDataSource::BTDataSource(const QUrl &srcUrl, QObject *parent) : TransferDataSource(srcUrl, parent), m_offset(0), m_bytes(0), m_torrentSource(QUrl()) { bt::InitLog(KStandardDirs::locateLocal("appdata", "torrentlog.log"));//initialize the torrent-log bt::SetClientInfo("KGet",2,1,0,bt::NORMAL,"KG");//Set client info to KGet, WARNING: Pls change this for every release bt::Uint16 i = 0; do { qCDebug(KGET_DEBUG) << "Trying to set port to" << BittorrentSettings::port() + i; bt::Globals::instance().initServer(BittorrentSettings::port() + i); i++; }while (!bt::Globals::instance().getServer().isOK() && i < 10); if (!bt::Globals::instance().getServer().isOK()) return; tc = new TorrentControl(); csf = new BTChunkSelectorFactory(); cf = new BTCacheFactory(); connect(cf, SIGNAL(cacheAdded(BTCache*)), SLOT(cacheAdded(BTCache*))); connect(csf, SIGNAL(selectorAdded(BTChunkSelector*)), SLOT(selectorAdded(BTChunkSelector*))); tc->setChunkSelectorFactory(csf); tc->setCacheFactory(cf); connect(&timer, SIGNAL(timeout()), SLOT(update())); } BTDataSource::~BTDataSource() { delete tc; delete cs; delete cf; } void BTDataSource::cacheAdded(BTCache *cache) { connect(cache, SIGNAL(dataArrived(KIO::fileoffset_t,QByteArray)), SLOT(getData(KIO::fileoffset_t,QByteArray))); } void BTDataSource::selectorAdded(BTChunkSelector* selector) { cs = selector; } void BTDataSource::start() { if (m_torrentSource.isEmpty()) { Download *download = new Download(m_source, KStandardDirs::locateLocal("appdata", "tmp/") + m_source.fileName()); connect(download, SIGNAL(finishedSuccessfully(QUrl,QByteArray)), SLOT(init(QUrl,QByteArray))); } else { cs->excludeAll(); const BitSet & bits = tc->availableChunksBitSet(); bool av = true; Uint32 firstChunk = m_offset / tc->getStats().chunk_size; Uint32 lastChunk = ((m_offset + m_bytes) / tc->getStats().chunk_size) + 1;//The +1 is only a workaround for rounding up, but I dunno how to do it ;) for (int i = firstChunk * tc->getStats().chunk_size * 8; i <= lastChunk * tc->getStats().chunk_size * 8; i++) { if (!bits.get(i)) { emit broken(); av = false; continue; } } if (av) { cs->reincluded(firstChunk, lastChunk); tc->start(); timer.start(250); } } } void BTDataSource::stop() { tc->stop(true); timer.stop(); } void BTDataSource::update() { bt::UpdateCurrentTime(); bt::AuthenticationMonitor::instance().update(); tc->update(); } void BTDataSource::init(const QUrl &torrentSource, const QByteArray &data) { Q_UNUSED(data) m_torrentSource = torrentSource; try { tc->init(0, m_torrentSource.url(), QString(), QString(), 0); } catch (bt::Error &err) { qCDebug(KGET_DEBUG) << err.toString(); //m_ready = false; } start(); } void BTDataSource::addSegment(const KIO::fileoffset_t offset, const KIO::fileoffset_t bytes, int segmentNum) { qCDebug(KGET_DEBUG); if (offset < m_offset) { m_offset = offset; if (m_bytes < bytes + m_offset - offset) { m_bytes = bytes + m_offset - offset; } } if (offset > m_offset && m_bytes < bytes + m_offset - offset) { m_bytes = bytes + m_offset - offset; } if (offset == m_offset && m_bytes < bytes) { m_bytes = bytes; } } void BTDataSource::getData(const KIO::fileoffset_t &off, const QByteArray &dataArray) { QByteArray splittedData; if (off < m_offset) splittedData = dataArray.right(dataArray.size() - (m_offset - off)); else if (m_offset + m_bytes < off + dataArray.size()) splittedData = dataArray.left((off + dataArray.size()) - (m_offset + m_bytes)); else splittedData = dataArray; emit data(off, splittedData); if (m_offset + m_bytes == off + dataArray.size()) emit finished(); } diff --git a/transfer-plugins/bittorrent/btdatasource.h b/transfer-plugins/bittorrent/btdatasource.h index 2af239d2..502959a2 100644 --- a/transfer-plugins/bittorrent/btdatasource.h +++ b/transfer-plugins/bittorrent/btdatasource.h @@ -1,60 +1,60 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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. */ #ifndef BTDATASOURCE_H #define BTDATASOURCE_H #include "core/transferdatasource.h" -#include +#include #include #include namespace bt { class TorrentControl; } class BTChunkSelectorFactory; class BTChunkSelector; class BTCacheFactory; class BTCache; class BTDataSource : public TransferDataSource { Q_OBJECT public: BTDataSource(const QUrl &srcUrl, QObject *parent); ~BTDataSource(); void start(); void stop(); void addSegments(const QPair &segmentSize, const QPair &segmentRange); void getData(const KIO::fileoffset_t &off, const QByteArray &dataArray); private slots: void init(const QUrl &torrentSource, const QByteArray &data); void cacheAdded(BTCache *cache); void selectorAdded(BTChunkSelector *selector); void update(); private: bt::TorrentControl *tc; BTChunkSelectorFactory *csf; BTChunkSelector *cs; BTCacheFactory *cf; KIO::fileoffset_t m_offset; KIO::fileoffset_t m_bytes; QUrl m_torrentSource; QTimer timer; }; #endif diff --git a/transfer-plugins/bittorrent/btsettingswidget.cpp b/transfer-plugins/bittorrent/btsettingswidget.cpp index 1c185f18..8b3b00de 100644 --- a/transfer-plugins/bittorrent/btsettingswidget.cpp +++ b/transfer-plugins/bittorrent/btsettingswidget.cpp @@ -1,71 +1,72 @@ /* This file is part of the KDE project Copyright (C) 2007 Lukas Appelhans 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. */ #include "btsettingswidget.h" #include "kget_macro.h" #include "bittorrentsettings.h" #include "kget_debug.h" -#include -#include + +#include +#include KGET_EXPORT_PLUGIN_CONFIG(BTSettingsWidget) BTSettingsWidget::BTSettingsWidget(QWidget * parent, const QVariantList &args) : KCModule(/*KGetFactory::componentData(),*/ parent, args) { setupUi(this); connect(portBox, SIGNAL(valueChanged(int)), SLOT(changed())); connect(uploadBox, SIGNAL(valueChanged(int)), SLOT(changed())); connect(downloadBox, SIGNAL(valueChanged(int)), SLOT(changed())); connect(torrentEdit, SIGNAL(textChanged(QString)), SLOT(changed())); connect(tempEdit, SIGNAL(textChanged(QString)), SLOT(changed())); connect(preallocBox, SIGNAL(stateChanged(int)), SLOT(changed())); connect(utpBox, SIGNAL(stateChanged(int)), SLOT(changed())); } void BTSettingsWidget::load() { torrentEdit->setMode(KFile::Directory); torrentEdit->fileDialog()->setWindowTitle(i18n("Select a default torrent folder")); tempEdit->setMode(KFile::Directory); tempEdit->fileDialog()->setWindowTitle(i18n("Select a default temporary folder")); defaults(); } void BTSettingsWidget::save() { qCDebug(KGET_DEBUG) << "Save Bittorrent-config"; BittorrentSettings::setPort(portBox->value()); BittorrentSettings::setUploadLimit(uploadBox->value()); BittorrentSettings::setDownloadLimit(downloadBox->value()); BittorrentSettings::setTorrentDir(torrentEdit->url().url()); BittorrentSettings::setTmpDir(tempEdit->url().url()); BittorrentSettings::setPreAlloc(preallocBox->isChecked()); BittorrentSettings::setEnableUTP(utpBox->isChecked()); BittorrentSettings::self()->save(); } void BTSettingsWidget::defaults() { portBox->setValue(BittorrentSettings::port()); uploadBox->setValue(BittorrentSettings::uploadLimit()); downloadBox->setValue(BittorrentSettings::downloadLimit()); torrentEdit->setUrl(BittorrentSettings::torrentDir()); tempEdit->setUrl(BittorrentSettings::tmpDir()); preallocBox->setChecked(BittorrentSettings::preAlloc()); utpBox->setChecked(BittorrentSettings::enableUTP()); } #include "btsettingswidget.moc" diff --git a/transfer-plugins/bittorrent/bttransfer.cpp b/transfer-plugins/bittorrent/bttransfer.cpp index 98aa071c..af75c601 100644 --- a/transfer-plugins/bittorrent/bttransfer.cpp +++ b/transfer-plugins/bittorrent/bttransfer.cpp @@ -1,829 +1,829 @@ /* This file is part of the KDE project Copyright (C) 2007-2008 Lukas Appelhans Copyright (C) 2007 Joris Guisson 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. */ #include "bttransfer.h" #include "bittorrentsettings.h" #include "bttransferhandler.h" //#include "btchunkselector.h" #include "advanceddetails/monitor.h" #include "core/kget.h" #include "core/filemodel.h" #include "core/download.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kget_debug.h" -#include #include #include #include #include -#include #include -#include +#include +#include #include +#include #include -#include +#include #ifdef ERROR #undef ERROR #endif BTTransfer::BTTransfer(TransferGroup* parent, TransferFactory* factory, Scheduler* scheduler, const QUrl &src, const QUrl& dest, const QDomElement * e) : Transfer(parent, factory, scheduler, src, dest, e), torrent(nullptr), m_tmp(KStandardDirs::locateLocal("appdata", "tmp/")), m_ready(false), m_downloadFinished(false), m_movingFile(false), m_fileModel(nullptr), m_updateCounter(0) { m_directory = KIO::upUrl(m_dest);//FIXME test setCapabilities(Transfer::Cap_Moving | Transfer::Cap_Renaming | Transfer::Cap_Resuming | Transfer::Cap_SpeedLimit); } BTTransfer::~BTTransfer() { if (torrent && m_ready) torrent->setMonitor(nullptr); delete torrent; } void BTTransfer::deinit(Transfer::DeleteOptions options) { kDebug() << "****************************DEINIT"; if (torrent && (options & Transfer::DeleteFiles)) {//FIXME: Also delete when torrent does not exist torrent->deleteDataFiles(); } if (options & Transfer::DeleteTemporaryFiles) { QDir tmpDir(m_tmp); qCDebug(KGET_DEBUG) << m_tmp + m_source.fileName().remove(".torrent"); tmpDir.rmdir(m_source.fileName().remove(".torrent") + "/dnd"); tmpDir.cd(m_source.fileName().remove(".torrent")); QStringList list = tmpDir.entryList(); foreach (const QString &file, list) { tmpDir.remove(file); } tmpDir.cdUp(); tmpDir.rmdir(m_source.fileName().remove(".torrent")); //only remove the .torrent file if it was downloaded by KGet if (!m_tmpTorrentFile.isEmpty()) { qCDebug(KGET_DEBUG) << "Removing" << m_tmpTorrentFile; QFile torrentFile(m_tmpTorrentFile); torrentFile.remove(); } } } /** Reimplemented functions from Transfer-Class **/ bool BTTransfer::isStalled() const { return (status() == Job::Running) && (downloadSpeed() == 0) && torrent && torrent->getStats().status == bt::STALLED; } bool BTTransfer::isWorking() const { if (!torrent) return false; const bt::TorrentStats stats = torrent->getStats(); return (stats.status != bt::ERROR) && (stats.status != bt::STALLED) && (stats.status != bt::NO_SPACE_LEFT) && (stats.status != bt::INVALID_STATUS); } void BTTransfer::start() { if (m_movingFile) { return; } if (!torrent) { if (!m_source.isLocalFile()) { qCDebug(KGET_DEBUG) << m_dest.path(); m_tmpTorrentFile = QString(KStandardDirs::locateLocal("appdata", "tmp/") + m_dest.fileName()); Download *download = new Download(m_source, QUrl::fromLocalFile(m_tmpTorrentFile)); setStatus(Job::Stopped, i18n("Downloading Torrent File...."), SmallIcon("document-save")); setTransferChange(Tc_Status, true); //m_source = KStandardDirs::locateLocal("appdata", "tmp/") + m_source.fileName(); connect(download, SIGNAL(finishedSuccessfully(QUrl,QByteArray)), SLOT(btTransferInit(QUrl,QByteArray))); } else btTransferInit(); } else startTorrent(); } bool BTTransfer::setDirectory(const QUrl &newDirectory) { //check if the newDestination is the same as the old QUrl temp = newDirectory; temp = temp.adjusted(QUrl::StripTrailingSlash); temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); if (newDirectory.isValid() && (newDirectory != dest()) && (temp != dest())) { if (torrent->changeOutputDir(newDirectory.url(QUrl::PreferLocalFile), bt::TorrentInterface::MOVE_FILES)) { connect(torrent, SIGNAL(aboutToBeStarted(bt::TorrentInterface*,bool&)), this, SLOT(newDestResult())); m_movingFile = true; m_directory = newDirectory; m_dest = m_directory; m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name)); setStatus(Job::Stopped, i18nc("changing the destination of the file", "Changing destination"), SmallIcon("media-playback-pause")); setTransferChange(Tc_Status, true); return true; } } m_movingFile = false; return false; } void BTTransfer::newDestResult() { disconnect(torrent, SIGNAL(aboutToBeStarted(bt::TorrentInterface*,bool&)), this, SLOT(newDestResult())); m_movingFile = false; setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), SmallIcon("media-playback-start")); setTransferChange(Tc_FileName | Tc_Status, true); } void BTTransfer::stop() { if (m_movingFile) return; if (m_ready) { stopTorrent(); } } /**Own public functions**/ void BTTransfer::update() { if (m_movingFile) { return; } if (torrent) { QStringList files; if (torrent->hasMissingFiles(files)) { torrent->recreateMissingFiles(); } updateTorrent(); } else timer.stop(); } void BTTransfer::load(const QDomElement *element) { Transfer::load(element); if ((m_totalSize == m_downloadedSize) && (m_totalSize != 0)) { setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), SmallIcon("dialog-ok")); } } // void BTTransfer::save(const QDomElement &element) // { // qCDebug(KGET_DEBUG); // // QDomElement e = element; // // Transfer::save(e); // } /**Public functions of BTTransfer**/ void BTTransfer::setPort(int port) { bt::Globals::instance().getTCPServer().changePort(port); if (BittorrentSettings::enableUTP()) bt::Globals::instance().getUTPServer().changePort(port + 1); } void BTTransfer::setSpeedLimits(int ulLimit, int dlLimit) { qCDebug(KGET_DEBUG); if (!torrent) return; torrent->setTrafficLimits(ulLimit * 1000, dlLimit * 1000); } void BTTransfer::addTracker(const QString &url) { qCDebug(KGET_DEBUG); if(torrent->getStats().priv_torrent) { KMessageBox::sorry(nullptr, i18n("Cannot add a tracker to a private torrent.")); return; } if(!QUrl(url).isValid()) { KMessageBox::error(nullptr, i18n("Malformed URL.")); return; } torrent->getTrackersList()->addTracker(url,true); } /**Private functions**/ void BTTransfer::startTorrent() { if (m_ready) { //qCDebug(KGET_DEBUG) << "Going to download that stuff :-0"; setSpeedLimits(uploadLimit(Transfer::InvisibleSpeedLimit), downloadLimit(Transfer::InvisibleSpeedLimit));//Set traffic-limits before starting torrent->setMonitor(this); torrent->start(); timer.start(250); if (chunksTotal() == chunksDownloaded()/* && !m_downloadFinished*/) { slotDownloadFinished(torrent); } else { setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), SmallIcon("media-playback-start")); } m_totalSize = torrent->getStats().total_bytes_to_download; setTransferChange(Tc_Status | Tc_TrackersList | Tc_TotalSize, true); updateFilesStatus(); } } void BTTransfer::stopTorrent() { torrent->stop(); torrent->setMonitor(nullptr); m_downloadSpeed = 0; timer.stop(); if (m_downloadFinished) { setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), SmallIcon("dialog-ok")); } else { setStatus(Job::Stopped, i18nc("transfer state: stopped", "Stopped"), SmallIcon("process-stop")); } setTransferChange(Tc_Status, true); updateFilesStatus(); } void BTTransfer::updateTorrent() { //qCDebug(KGET_DEBUG) << "Update torrent"; bt::UpdateCurrentTime(); bt::AuthenticationMonitor::instance().update(); torrent->update(); ChangesFlags changesFlags = 0; if (m_downloadedSize != (m_downloadedSize = torrent->getStats().bytes_downloaded)) changesFlags |= Tc_DownloadedSize; if (m_uploadSpeed != static_cast(torrent->getStats().upload_rate)) { m_uploadSpeed = torrent->getStats().upload_rate; changesFlags |= Tc_UploadSpeed; } if (m_downloadSpeed != static_cast(torrent->getStats().download_rate)) { m_downloadSpeed = torrent->getStats().download_rate; changesFlags |= Tc_DownloadSpeed; } int percent = (chunksDownloaded() * 100) / chunksTotal(); if (m_percent != percent) { m_percent = percent; changesFlags |= Tc_Percent; } setTransferChange(changesFlags, true); //update the files status every 3 seconds if (!m_updateCounter) { updateFilesStatus(); m_updateCounter = 12; } --m_updateCounter; } void BTTransfer::updateFilesStatus() { const Job::Status currentStatus = this->status(); if (!torrent) { return; } const bt::TorrentStats *stats = &torrent->getStats(); if (stats->multi_file_torrent) { QHash::const_iterator it; QHash::const_iterator itEnd = m_files.constEnd(); for (it = m_files.constBegin(); it != itEnd; ++it) { QModelIndex status = m_fileModel->index(it.key(), FileItem::Status); if (!(*it)->doNotDownload() && (currentStatus == Job::Running)) { m_fileModel->setData(status, Job::Running); } else { m_fileModel->setData(status, Job::Stopped); } if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) { m_fileModel->setData(status, Job::Finished); } } } else { QModelIndexList indexes = fileModel()->fileIndexes(FileItem::Status); if (indexes.count() != 1) { return; } QModelIndex index = indexes.first(); if (stats->bytes_left_to_download) { if (currentStatus == Job::Running) { fileModel()->setData(index, Job::Running); } else { fileModel()->setData(index, Job::Stopped); } } else { fileModel()->setData(index, Job::Finished); } } } void BTTransfer::btTransferInit(const QUrl &src, const QByteArray &data) { Q_UNUSED(data) qCDebug(KGET_DEBUG); if (src != m_source && !src.isEmpty()) m_source = src; QFile file(m_source.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { setError(i18n("Torrent file does not exist"), SmallIcon("dialog-cancel"), Job::NotSolveable); setTransferChange(Tc_Status, true); return; } setStatus(Job::Stopped, i18n("Analyzing torrent...."), SmallIcon("document-preview")); // jpetso says: you should probably use the "process-working" icon here (from the animations category), but that's a multi-frame PNG so it's hard for me to test setTransferChange(Tc_Status, true); bt::InitLog(KStandardDirs::locateLocal("appdata", "torrentlog.log"), false, false);//initialize the torrent-log bt::SetClientInfo("KGet", 2, 95, 0, bt::NORMAL, "KG");//Set client info to KGet TODO: don't hardcode version number bt::Uint16 i = 0; while (!bt::Globals::instance().initTCPServer(BittorrentSettings::port() + i) && i < 10) i++; if (i == 10) { setError(i18n("Cannot initialize port..."), SmallIcon("dialog-cancel")); setTransferChange(Tc_Status); return; } if (BittorrentSettings::enableUTP()) { while (!bt::Globals::instance().initUTPServer(BittorrentSettings::port() + i) && i < 10) //We don't care if it fails for now as UTP is experimental... i++; } QDir tmpDir(m_tmp + m_source.fileName().remove(".torrent")); if (tmpDir.exists()) { tmpDir.remove("torrent"); } try { torrent = new bt::TorrentControl(); if (!BittorrentSettings::tmpDir().isEmpty() && QFileInfo(BittorrentSettings::tmpDir()).isDir()) { m_tmp = BittorrentSettings::tmpDir(); } m_ready = true; kDebug() << "Source:" << m_source.path() << "Destination:" << m_dest.path(); m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); torrent->init(nullptr, file.readAll(), m_tmp + m_source.fileName().remove(".torrent"), QUrl::fromLocalFile(m_dest.adjusted(QUrl::RemoveFilename).path()).toLocalFile()); m_dest = QUrl::fromLocalFile(torrent->getStats().output_path); if (!torrent->getStats().multi_file_torrent && (m_dest.fileName() != torrent->getStats().torrent_name))//TODO check if this is needed, so if that case is true at some point { m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name)); } torrent->createFiles(); torrent->setPreallocateDiskSpace(BittorrentSettings::preAlloc()); connect(torrent, SIGNAL(stoppedByError(bt::TorrentInterface*,QString)), SLOT(slotStoppedByError(bt::TorrentInterface*,QString))); connect(torrent, SIGNAL(finished(bt::TorrentInterface*)), this, SLOT(slotDownloadFinished(bt::TorrentInterface*))); //FIXME connect(tc,SIGNAL(corruptedDataFound(bt::TorrentInterface*)), this, SLOT(emitCorruptedData(bt::TorrentInterface*)));//TODO: Fix it } catch (bt::Error &err) { m_ready = false; torrent->deleteLater(); torrent = nullptr; setError(err.toString(), SmallIcon("dialog-cancel"), Job::NotSolveable); setTransferChange(Tc_Status); return; } startTorrent(); connect(&timer, SIGNAL(timeout()), SLOT(update())); } void BTTransfer::slotStoppedByError(const bt::TorrentInterface* &error, const QString &errormsg) { Q_UNUSED(error) stop(); setError(errormsg, SmallIcon("dialog-cancel"), Job::NotSolveable); setTransferChange(Tc_Status); } void BTTransfer::slotDownloadFinished(bt::TorrentInterface* ti) { qCDebug(KGET_DEBUG) << "Start seeding *********************************************************************"; Q_UNUSED(ti) m_downloadFinished = true; //timer.stop(); setStatus(Job::FinishedKeepAlive, i18nc("Transfer status: seeding", "Seeding...."), SmallIcon("media-playback-start")); setTransferChange(Tc_Status, true); } /**Property-Functions**/ QList BTTransfer::trackersList() const { if (!torrent) return QList(); QList trackers; foreach (bt::TrackerInterface * tracker, torrent->getTrackersList()->getTrackers()) trackers << tracker->trackerURL(); return trackers; } int BTTransfer::sessionBytesDownloaded() const { if (!torrent) return -1; return torrent->getStats().session_bytes_downloaded; } int BTTransfer::sessionBytesUploaded() const { if (!torrent) return -1; return torrent->getStats().session_bytes_uploaded; } int BTTransfer::chunksTotal() const { if (!torrent) return -1; return torrent->getTorrent().getNumChunks(); } int BTTransfer::chunksDownloaded() const { if (!torrent) return -1; return torrent->downloadedChunksBitSet().numOnBits(); } int BTTransfer::chunksExcluded() const { if (!torrent) return -1; return torrent->excludedChunksBitSet().numOnBits(); } int BTTransfer::chunksLeft() const { if (!torrent) return -1; return chunksTotal() - chunksDownloaded(); } int BTTransfer::seedsConnected() const { if (!torrent) return -1; return torrent->getStats().seeders_connected_to; } int BTTransfer::seedsDisconnected() const { if (!torrent) return -1; return torrent->getStats().seeders_total; } int BTTransfer::leechesConnected() const { if (!torrent) return -1; return torrent->getStats().leechers_connected_to; } int BTTransfer::leechesDisconnected() const { if (!torrent) return -1; return torrent->getStats().leechers_total; } int BTTransfer::elapsedTime() const { if (!torrent) return -1; return torrent->getRunningTimeDL(); } int BTTransfer::remainingTime() const { if (!torrent) return Transfer::remainingTime(); return torrent->getETA(); } bt::TorrentControl * BTTransfer::torrentControl() { return torrent; } bool BTTransfer::ready() { return m_ready; } void BTTransfer::downloadRemoved(bt::ChunkDownloadInterface* cd) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->downloadRemoved(cd); setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true); } void BTTransfer::downloadStarted(bt::ChunkDownloadInterface* cd) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->downloadStarted(cd); setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true); } void BTTransfer::peerAdded(bt::PeerInterface* peer) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->peerAdded(peer); setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true); } void BTTransfer::peerRemoved(bt::PeerInterface* peer) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->peerRemoved(peer); setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true); } void BTTransfer::stopped() { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->stopped(); } void BTTransfer::destroyed() { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->destroyed(); } QList BTTransfer::files() const { QList urls; if (!torrent) { return urls; } //multiple files if (torrent->getStats().multi_file_torrent) { for (uint i = 0; i < torrent->getNumFiles(); ++i) { const QString path = torrent->getTorrentFile(i).getPathOnDisk(); urls.append(QUrl(path)); } } //one single file else { QUrl temp = m_dest; if (m_dest.fileName() != torrent->getStats().torrent_name)//TODO check if the body is ever entered! { temp = temp.adjusted(QUrl::StripTrailingSlash); temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); } urls.append(temp); } return urls; } void BTTransfer::filesSelected() { QModelIndexList indexes = fileModel()->fileIndexes(FileItem::File); //one single file if (indexes.count() == 1) { QModelIndex index = indexes.first(); const bool doDownload = index.data(Qt::CheckStateRole).toBool(); if (torrent && torrent->getStats().bytes_left_to_download) { if (doDownload) { start(); } else { stop(); } } } //multiple files else { foreach (const QModelIndex &index, indexes) { const QUrl dest = fileModel()->getUrl(index); const bool doDownload = index.data(Qt::CheckStateRole).toBool(); bt::TorrentFileInterface *file = m_files[dest]; file->setDoNotDownload(!doDownload); } } // setTransferChange(Tc_TotalSize | Tc_DownloadedSize | Tc_Percent, true); } FileModel *BTTransfer::fileModel()//TODO correct file model for one-file-torrents { if (!m_fileModel) { if (!torrent) { return 0; } //multiple files if (torrent->getStats().multi_file_torrent) { for (bt::Uint32 i = 0; i < torrent->getNumFiles(); ++i) { bt::TorrentFileInterface *file = &torrent->getTorrentFile(i); m_files[QUrl(file->getPathOnDisk())] = file; } m_fileModel = new FileModel(m_files.keys(), directory(), this); // connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); connect(m_fileModel, SIGNAL(checkStateChanged()), this, SLOT(filesSelected())); //set the checkstate, the status and the size of the model items QHash::const_iterator it; QHash::const_iterator itEnd = m_files.constEnd(); const Job::Status curentStatus = this->status(); for (it = m_files.constBegin(); it != itEnd; ++it) { QModelIndex size = m_fileModel->index(it.key(), FileItem::Size); m_fileModel->setData(size, static_cast((*it)->getSize())); const bool doDownload = !(*it)->doNotDownload(); QModelIndex checkIndex = m_fileModel->index(it.key(), FileItem::File); const Qt::CheckState checkState = doDownload ? Qt::Checked : Qt::Unchecked; m_fileModel->setData(checkIndex, checkState, Qt::CheckStateRole); QModelIndex status = m_fileModel->index(it.key(), FileItem::Status); if (doDownload && (curentStatus == Job::Running)) { m_fileModel->setData(status, Job::Running); } else { m_fileModel->setData(status, Job::Stopped); } if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) { m_fileModel->setData(status, Job::Finished); } } } //one single file else { QList urls; QUrl temp = m_dest; if (m_dest.fileName() != torrent->getStats().torrent_name)//TODO check if the body is ever entered! { temp = temp.adjusted(QUrl::StripTrailingSlash); temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); } const QUrl url = temp; urls.append(url); m_fileModel = new FileModel(urls, directory(), this); // connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); connect(m_fileModel, SIGNAL(checkStateChanged()), this, SLOT(filesSelected())); QModelIndex size = m_fileModel->index(url, FileItem::Size); m_fileModel->setData(size, static_cast(torrent->getStats().total_bytes)); QModelIndex checkIndex = m_fileModel->index(url, FileItem::File); m_fileModel->setData(checkIndex, Qt::Checked, Qt::CheckStateRole); QModelIndex status = m_fileModel->index(url, FileItem::Status); if (this->status() == Job::Running) { m_fileModel->setData(status, Job::Running); } else { m_fileModel->setData(status, Job::Stopped); } if (!torrent->getStats().bytes_left_to_download) { m_fileModel->setData(status, Job::Finished); } } } return m_fileModel; } diff --git a/transfer-plugins/bittorrent/bttransferfactory.cpp b/transfer-plugins/bittorrent/bttransferfactory.cpp index 1dfe6f3a..f0ddc30f 100644 --- a/transfer-plugins/bittorrent/bttransferfactory.cpp +++ b/transfer-plugins/bittorrent/bttransferfactory.cpp @@ -1,117 +1,118 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2010 Lukas Appelhans 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. */ #include "bttransferfactory.h" // header inclusion order is crucial because of signal emit clashes #include "bttransfer.h" #include "btdatasource.h" #include "bttransferhandler.h" #include "btdetailswidget.h" #include "advanceddetails/btadvanceddetailswidget.h" #include "kget_debug.h" -#include #include #include #include +#include + K_PLUGIN_FACTORY_WITH_JSON(KGetFactory, "kget_bittorrentfactory.json", registerPlugin();) BTTransferFactory::BTTransferFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) { if (!bt::InitLibKTorrent()) { qCCritical(KGET_DEBUG) << "Failed to initialize libktorrent"; KGet::showNotification(nullptr, "error", i18n("Cannot initialize libktorrent. Torrent support might not work.")); } } BTTransferFactory::~BTTransferFactory() { } Transfer * BTTransferFactory::createTransfer(const QUrl &srcUrl, const QUrl &destUrl, TransferGroup * parent, Scheduler * scheduler, const QDomElement * e ) { qCDebug(KGET_DEBUG) << "BTTransferFactory::createTransfer"; if (isSupported(srcUrl)) { return new BTTransfer(parent, this, scheduler, srcUrl, destUrl, e); } return 0; } TransferHandler * BTTransferFactory::createTransferHandler(Transfer * transfer, Scheduler * scheduler) { BTTransfer * bttransfer = qobject_cast(transfer); if (!bttransfer) { qCCritical(KGET_DEBUG) << "WARNING! passing a non-BTTransfer pointer!!"; return 0; } return new BTTransferHandler(bttransfer, scheduler); } QWidget * BTTransferFactory::createDetailsWidget( TransferHandler * transfer ) { BTTransferHandler * bttransfer = static_cast(transfer); return new BTDetailsWidget(bttransfer); } const QList BTTransferFactory::actions(TransferHandler *handler) { BTTransferHandler * bttransfer = static_cast(handler); QList actions; if (bttransfer && bttransfer->torrentControl()) { QAction *openAdvancedDetailsAction = new QAction(QIcon::fromTheme("document-open"), i18n("&Advanced Details"), this); connect(openAdvancedDetailsAction, SIGNAL(triggered()), bttransfer, SLOT(createAdvancedDetails())); actions.append(openAdvancedDetailsAction); QAction *openScanDlg = new QAction(QIcon::fromTheme("document-open"), i18n("&Scan Files"), this); connect(openScanDlg, SIGNAL(triggered()), bttransfer, SLOT(createScanDlg())); actions.append(openScanDlg); } if (bttransfer) return actions; else return QList(); } TransferDataSource * BTTransferFactory::createTransferDataSource(const QUrl &srcUrl, const QDomElement &type, QObject *parent) { Q_UNUSED(srcUrl) Q_UNUSED(type) Q_UNUSED(parent) /*if (srcUrl.fileName().endsWith(".torrent")) return new BTDataSource();*/ return 0; } bool BTTransferFactory::isSupported(const QUrl &url) const { return url.url().endsWith(QLatin1String(".torrent")); } #include "bttransferfactory.moc" diff --git a/transfer-plugins/bittorrent/scandlg.cpp b/transfer-plugins/bittorrent/scandlg.cpp index 163188f8..5cd55978 100644 --- a/transfer-plugins/bittorrent/scandlg.cpp +++ b/transfer-plugins/bittorrent/scandlg.cpp @@ -1,115 +1,118 @@ /*************************************************************************** * 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 "scandlg.h" -#include -#include -#include -#include #include #include #include + +#include +#include +#include + +#include + using namespace bt; namespace kt { ScanDlg::ScanDlg(KJob *job, QWidget* parent) : KDialog(parent), m_job(static_cast(job)) { setButtons(KDialog::None); Ui::ScanDlgBase ui; QWidget *widget = new QWidget(this); ui.setupUi(widget); setMainWidget(widget); m_torrent_label = ui.torrent_label; m_chunks_found = ui.chunks_found; m_chunks_failed = ui.chunks_failed; m_chunks_downloaded = ui.chunks_downloaded; m_chunks_not_downloaded = ui.chunks_not_downloaded; m_progress = ui.progress; m_cancel = ui.cancel; KGuiItem::assign(m_cancel, KStandardGuiItem::cancel()); connect(m_cancel,SIGNAL(clicked()),this,SLOT(reject())); m_progress->setMaximum(100); m_progress->setValue(0); connect(m_job, SIGNAL(description(KJob*,QString,QPair,QPair)), SLOT(description(KJob*,QString,QPair,QPair))); connect(m_job, SIGNAL(result(KJob*)), SLOT(result(KJob*))); connect(m_job, SIGNAL(percent(KJob*,ulong)), SLOT(percent(KJob*,ulong))); } ScanDlg::~ScanDlg() { } void ScanDlg::closeEvent(QCloseEvent* ) { if (m_job) { m_job->kill(false); m_job = nullptr; } else accept(); } void ScanDlg::reject() { if (m_job) { m_job->kill(false); m_job = nullptr; } KDialog::reject(); deleteLater(); } void ScanDlg::accept() { KDialog::accept(); deleteLater(); } void ScanDlg::description(KJob *job, const QString &title, const QPair &field1, const QPair< QString, QString > &field2) { m_chunks_found->setText(field1.first); m_chunks_failed->setText(field1.second); m_chunks_downloaded->setText(field1.first); m_chunks_not_downloaded->setText(field2.second); } void ScanDlg::result(KJob *job) { if (job->error() && job->error() != KIO::ERR_USER_CANCELED) { KMessageBox::error(nullptr,i18n("Error scanning data: %1",job->errorString())); } m_job = nullptr; m_progress->setValue(100); disconnect(m_cancel,SIGNAL(clicked()),this,SLOT(reject())); connect(m_cancel,SIGNAL(clicked()),this,SLOT(accept())); } void ScanDlg::percent(KJob *job, unsigned long percent) { m_progress->setValue(percent); } } diff --git a/transfer-plugins/bittorrent/scandlg.h b/transfer-plugins/bittorrent/scandlg.h index d121daab..97dab99b 100644 --- a/transfer-plugins/bittorrent/scandlg.h +++ b/transfer-plugins/bittorrent/scandlg.h @@ -1,72 +1,73 @@ /*************************************************************************** * 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 KT_SCANDLG_HH #define KT_SCANDLG_HH #include #include #include + #include #include #include "ui_scandlg.h" namespace bt { class TorrentInterface; } namespace kt { class TorrentInterface; class ScanDlg : public KDialog { Q_OBJECT public: ScanDlg(KJob *job, QWidget* parent); virtual ~ScanDlg(); protected: /// Handle the close event virtual void closeEvent(QCloseEvent* e); protected slots: virtual void reject(); virtual void accept(); private slots: void description(KJob *job, const QString &title, const QPair &field1, const QPair< QString, QString > &field2); void result(KJob *job); void percent(KJob *job, unsigned long percent); private: bt::Job * m_job; QProgressBar *m_progress; QPushButton *m_cancel; QLabel *m_torrent_label; QLabel *m_chunks_failed; QLabel *m_chunks_found; QLabel *m_chunks_not_downloaded; QLabel *m_chunks_downloaded; }; } #endif diff --git a/transfer-plugins/contentfetch/contentfetch.cpp b/transfer-plugins/contentfetch/contentfetch.cpp index e51dd401..7481f46a 100644 --- a/transfer-plugins/contentfetch/contentfetch.cpp +++ b/transfer-plugins/contentfetch/contentfetch.cpp @@ -1,116 +1,116 @@ /* This file is part of the KDE project Copyright (C) 2008 Ningyu Shi 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. */ #include "contentfetch.h" #include "core/kget.h" #include "core/transfergroup.h" #include "script.h" -#include -#include #include +#include +#include #include #include #include #include #include #include #include ContentFetch::ContentFetch(TransferGroup* parent, TransferFactory* factory, Scheduler* scheduler, const KUrl& source, const KUrl& dest, const QString &scriptFile, const QDomElement* e) : Transfer(parent, factory, scheduler, source, dest, e), m_p_group(parent), m_scriptFile(scriptFile), m_destDir(dest.directory(KUrl::AppendTrailingSlash)) { m_p_script = new Script(this, source); connect(m_p_script, SIGNAL(newTransfer(QString,QString)), this, SLOT(slotAddTransfer(QString,QString))); connect(m_p_script, SIGNAL(finished()), this, SLOT(slotFinish())); connect(m_p_script, SIGNAL(aborted(QString)), this, SLOT(slotAbort(QString))); connect(m_p_script, SIGNAL(percentUpdated(int)), this, SLOT(setPercent(int))); connect(m_p_script, SIGNAL(textStatusUpdated(QString)), this, SLOT(slotSetTextStatus(QString))); } void ContentFetch::deinit() { return; } void ContentFetch::start() { qCDebug(KGET_DEBUG) << "ContentFetch::start"; setStatus(Job::Running, i18nc("Transfer state: processing script", "Processing script...."), SmallIcon("media-playback-start")); setTransferChange(Tc_Status, true); m_p_script->setFile(m_scriptFile); m_p_script->start(); qCDebug(KGET_DEBUG) << "ContentFetch::start() finished!"; } void ContentFetch::stop() { if(status() == Stopped) { return; } qCDebug(KGET_DEBUG) << "ContentFetch::stop"; // kill -9 the script m_p_script->terminate(); // delete m_p_script to avoid crash? setStatus(Job::Stopped, i18nc("transfer state: stopped", "Stopped"), SmallIcon("process-stop")); setTransferChange(Tc_Status, true); } void ContentFetch::slotAddTransfer(const QString &url, const QString &filename) { // even if filename is empty it's still ok kDebug() << "The whole filename is " << m_destDir + filename; KGet::addTransfer(KUrl(url), m_destDir + filename, m_p_group->name(), true); } void ContentFetch::slotFinish() { m_percent = 100; setStatus(Job::Finished, i18nc("Transfer State: Finished", "Finished"), SmallIcon("dialog-ok")); setTransferChange(Tc_Status|Tc_Percent, true); //delete m_p_script; } void ContentFetch::slotAbort(const QString &error) { if (error.isEmpty()) { setStatus(Job::Aborted, i18nc("Transfer State: Aborted", "Aborted"), SmallIcon("process-stop")); } else { setStatus(Job::Aborted, error, SmallIcon("process-stop")); } setTransferChange(Tc_Status, true); } void ContentFetch::slotSetTextStatus(const QString& text) { setStatus(Job::Running, text, SmallIcon("media-playback-start")); setTransferChange(Tc_Status, true); } bool ContentFetch::isResumable() const { return false; } void ContentFetch::setPercent(int percent) { m_percent = percent; setTransferChange( Transfer::Tc_Percent, true ); } diff --git a/transfer-plugins/multisegmentkio/transfermultisegkio.cpp b/transfer-plugins/multisegmentkio/transfermultisegkio.cpp index 0f3f52d4..c3e823f2 100644 --- a/transfer-plugins/multisegmentkio/transfermultisegkio.cpp +++ b/transfer-plugins/multisegmentkio/transfermultisegkio.cpp @@ -1,379 +1,380 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2006 Manolo Valdes 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. */ #include "transfermultisegkio.h" #include "multisegkiosettings.h" #include "core/kget.h" #include "core/transferdatasource.h" // #include "mirrors.h" #include "core/filemodel.h" #include "core/verifier.h" #include "core/signature.h" #include #include "kget_debug.h" -#include -#include + +#include #include #include -#include +#include #include +#include #include #include TransferMultiSegKio::TransferMultiSegKio(TransferGroup *parent, TransferFactory *factory, Scheduler *scheduler, const QUrl &source, const QUrl &dest, const QDomElement *e) : Transfer(parent, factory, scheduler, source, dest, e), m_movingFile(false), m_searchStarted(false), m_verificationSearch(false), m_dataSourceFactory(nullptr), m_fileModel(nullptr) { } void TransferMultiSegKio::init() { Transfer::init(); if (!m_dataSourceFactory) { m_dataSourceFactory = new DataSourceFactory(this, m_dest); connect(m_dataSourceFactory, SIGNAL(capabilitiesChanged()), this, SLOT(slotUpdateCapabilities())); connect(m_dataSourceFactory, SIGNAL(dataSourceFactoryChange(Transfer::ChangesFlags)), this, SLOT(slotDataSourceFactoryChange(Transfer::ChangesFlags))); connect(m_dataSourceFactory->verifier(), SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); connect(m_dataSourceFactory, SIGNAL(log(QString,Transfer::LogLevel)), this, SLOT(setLog(QString,Transfer::LogLevel))); m_dataSourceFactory->addMirror(m_source, MultiSegKioSettings::segments()); slotUpdateCapabilities(); } } void TransferMultiSegKio::deinit(Transfer::DeleteOptions options) { if (options & Transfer::DeleteFiles)//if the transfer is not finished, we delete the *.part-file { m_dataSourceFactory->deinit(); }//TODO: Ask the user if he/she wants to delete the *.part-file? To discuss (boom1992) } void TransferMultiSegKio::start() { qCDebug(KGET_DEBUG) << "Start TransferMultiSegKio"; if (status() == Running) { return; } m_dataSourceFactory->start(); if (MultiSegKioSettings::useSearchEngines() && !m_searchStarted) { m_searchStarted = true; QDomDocument doc; QDomElement element = doc.createElement("TransferDataSource"); element.setAttribute("type", "search"); doc.appendChild(element); TransferDataSource *mirrorSearch = KGet::createTransferDataSource(m_source, element, this); if (mirrorSearch) { connect(mirrorSearch, SIGNAL(data(QList)), this, SLOT(slotSearchUrls(QList))); mirrorSearch->start(); } } } void TransferMultiSegKio::stop() { qCDebug(KGET_DEBUG); if ((status() == Stopped) || (status() == Finished)) { return; } if (m_dataSourceFactory) { m_dataSourceFactory->stop(); } } bool TransferMultiSegKio::repair(const QUrl &file) { if (!file.isValid() || (m_dest == file)) { if (m_dataSourceFactory && (m_dataSourceFactory->verifier()->status() == Verifier::NotVerified)) { m_dataSourceFactory->repair(); return true; } } return false; } bool TransferMultiSegKio::setDirectory(const QUrl& newDirectory) { QUrl newDest = newDirectory; newDest.setPath(newDest.path() + "/" + m_dest.fileName()); return setNewDestination(newDest); } bool TransferMultiSegKio::setNewDestination(const QUrl &newDestination) { qCDebug(KGET_DEBUG) << "New destination: " << newDestination; if (newDestination.isValid() && (newDestination != dest()) && m_dataSourceFactory) { m_movingFile = true; stop(); m_dataSourceFactory->setNewDestination(newDestination); m_dest = newDestination; if (m_fileModel) { m_fileModel->setDirectory(directory()); } setTransferChange(Tc_FileName); return true; } return false; } void TransferMultiSegKio::load(const QDomElement *element) { qCDebug(KGET_DEBUG); Transfer::load(element); m_dataSourceFactory->load(element); } void TransferMultiSegKio::save(const QDomElement &element) { qCDebug(KGET_DEBUG); Transfer::save(element); m_dataSourceFactory->save(element); } void TransferMultiSegKio::slotDataSourceFactoryChange(Transfer::ChangesFlags change) { if (change & Tc_FileName) { QList urls = m_dataSourceFactory->mirrors().keys(); QString filename = urls.first().fileName(); if (filename.isEmpty()) return; foreach (const QUrl url, urls) { if (filename != url.fileName()) return; } QUrl path = m_dest.adjusted(QUrl::RemoveFilename); path.setPath(path.path() + filename); setNewDestination(path); } if (change & Tc_Source) { m_source = QUrl(); QHash< QUrl, QPair >::const_iterator it = m_dataSourceFactory->mirrors().constBegin(); QHash< QUrl, QPair >::const_iterator end = m_dataSourceFactory->mirrors().constEnd(); for (; it != end; it++) { if (it.value().first) { m_source = it.key(); break; } } } if (change & Tc_Status) { if ((m_dataSourceFactory->status() == Job::Finished) && m_source.scheme() == "ftp") { KIO::StatJob * statJob = KIO::stat(m_source); connect(statJob, SIGNAL(result(KJob*)), this, SLOT(slotStatResult(KJob*))); statJob->start(); } else { setStatus(m_dataSourceFactory->status()); } if (m_fileModel) { QModelIndex statusIndex = m_fileModel->index(m_dest, FileItem::Status); m_fileModel->setData(statusIndex, status()); } } if (change & Tc_TotalSize) { m_totalSize = m_dataSourceFactory->size(); if (m_fileModel) { QModelIndex sizeIndex = m_fileModel->index(m_dest, FileItem::Size); m_fileModel->setData(sizeIndex, static_cast(m_totalSize)); } } if (change & Tc_DownloadedSize) { KIO::filesize_t processedSize = m_dataSourceFactory->downloadedSize(); //only start the verification search _after_ data has come in, that way only connections //are requested if there is already a successful one if ((processedSize != m_downloadedSize) && !m_verificationSearch && MultiSegKioSettings::useSearchVerification()) { m_verificationSearch = true; QDomDocument doc; QDomElement element = doc.createElement("TransferDataSource"); element.setAttribute("type", "checksumsearch"); doc.appendChild(element); TransferDataSource *checksumSearch = KGet::createTransferDataSource(m_source, element, this); if (checksumSearch) { connect(checksumSearch, SIGNAL(data(QString,QString)), this, SLOT(slotChecksumFound(QString,QString))); checksumSearch->start(); } } m_downloadedSize = m_dataSourceFactory->downloadedSize(); } if (change & Tc_Percent) { m_percent = m_dataSourceFactory->percent(); } if (change & Tc_DownloadSpeed) { qCDebug(KGET_DEBUG) << "speed:" << m_downloadSpeed; m_downloadSpeed = m_dataSourceFactory->currentSpeed(); } setTransferChange(change, true); } void TransferMultiSegKio::slotVerified(bool isVerified) { if (m_fileModel) { QModelIndex checksumVerified = m_fileModel->index(m_dest, FileItem::ChecksumVerified); m_fileModel->setData(checksumVerified, verifier()->status()); } if (!isVerified) { QString text = i18n("The download (%1) could not be verified. Do you want to repair it?", m_dest.fileName()); if (!verifier()->partialChunkLength()) { text = i18n("The download (%1) could not be verified. Do you want to redownload it?", m_dest.fileName()); } if (KMessageBox::warningYesNo(nullptr, text, i18n("Verification failed.")) == KMessageBox::Yes) { repair(); } } } void TransferMultiSegKio::slotStatResult(KJob* kioJob) { KIO::StatJob * statJob = qobject_cast(kioJob); if (!statJob->error()) { const KIO::UDSEntry entryResult = statJob->statResult(); struct utimbuf time; time.modtime = entryResult.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME); time.actime = QDateTime::currentDateTime().toTime_t(); utime(m_dest.toLocalFile().toUtf8().constData(), &time); } setStatus(Job::Finished); setTransferChange(Tc_Status, true); } void TransferMultiSegKio::slotSearchUrls(const QList &urls) { qCDebug(KGET_DEBUG) << "Found " << urls.size() << " urls."; foreach (const QUrl &url, urls) { m_dataSourceFactory->addMirror(url, MultiSegKioSettings::segments()); } } void TransferMultiSegKio::slotChecksumFound(QString type, QString checksum) { m_dataSourceFactory->verifier()->addChecksum(type, checksum); } QHash > TransferMultiSegKio::availableMirrors(const QUrl &file) const { Q_UNUSED(file) return m_dataSourceFactory->mirrors(); } void TransferMultiSegKio::setAvailableMirrors(const QUrl &file, const QHash > &mirrors) { Q_UNUSED(file) m_dataSourceFactory->setMirrors(mirrors); m_source = QUrl(); QHash< QUrl, QPair >::const_iterator it = mirrors.begin(); QHash< QUrl, QPair >::const_iterator end = mirrors.end(); for (; it != end; it++) { if (it.value().first) { m_source = it.key(); break; } } setTransferChange(Tc_Source, true); } Verifier *TransferMultiSegKio::verifier(const QUrl &file) { Q_UNUSED(file) return m_dataSourceFactory->verifier(); } Signature *TransferMultiSegKio::signature(const QUrl &file) { Q_UNUSED(file) return m_dataSourceFactory->signature(); } FileModel *TransferMultiSegKio::fileModel() { if (!m_fileModel) { m_fileModel = new FileModel(QList() << m_dest, m_dest.adjusted(QUrl::RemoveFilename), this); connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); QModelIndex statusIndex = m_fileModel->index(m_dest, FileItem::Status); m_fileModel->setData(statusIndex, m_dataSourceFactory->status()); QModelIndex sizeIndex = m_fileModel->index(m_dest, FileItem::Size); m_fileModel->setData(sizeIndex, static_cast(m_dataSourceFactory->size())); QModelIndex checksumVerified = m_fileModel->index(m_dest, FileItem::ChecksumVerified); m_fileModel->setData(checksumVerified, verifier()->status()); QModelIndex signatureVerified = m_fileModel->index(m_dest, FileItem::SignatureVerified); m_fileModel->setData(signatureVerified, signature()->status()); } return m_fileModel; } void TransferMultiSegKio::slotRename(const QUrl &oldUrl, const QUrl &newUrl) { Q_UNUSED(oldUrl) if (newUrl.isValid() && (newUrl != dest()) && m_dataSourceFactory) { m_movingFile = true; stop(); m_dataSourceFactory->setNewDestination(newUrl); m_dest = newUrl; setTransferChange(Tc_FileName); } } void TransferMultiSegKio::slotUpdateCapabilities() { setCapabilities(m_dataSourceFactory->capabilities()); }