Make filepriorities working...
[kdenetwork.git] / kget / transfer-plugins / bittorrent / torrentfiletreemodel.cpp
blob104e921ca5688f617422334d701ecdc0525aad5f
1 /***************************************************************************
2 * Copyright (C) 2007 by Joris Guisson and Ivan Vasic *
3 * joris.guisson@gmail.com *
4 * ivasic@gmail.com *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 ***************************************************************************/
21 #include <klocale.h>
22 #include <kicon.h>
23 #include <kmimetype.h>
24 #include <QTreeView>
25 #include <bcodec/bdecoder.h>
26 #include <bcodec/bencoder.h>
27 #include <bcodec/bnode.h>
28 #include <interfaces/torrentinterface.h>
29 #include <interfaces/torrentfileinterface.h>
30 #include <util/functions.h>
31 #include <util/log.h>
32 #include "torrentfiletreemodel.h"
34 using namespace bt;
36 namespace kt
39 TorrentFileTreeModel::Node::Node(Node* parent,bt::TorrentFileInterface* file,const QString & name)
40 : parent(parent),file(file),name(name),size(0)
43 TorrentFileTreeModel::Node::Node(Node* parent,const QString & name)
44 : parent(parent),file(0),name(name),size(0)
48 TorrentFileTreeModel::Node::~Node()
50 qDeleteAll(children);
53 void TorrentFileTreeModel::Node::insert(const QString & path,bt::TorrentFileInterface* file)
55 int p = path.indexOf(bt::DirSeparator());
56 if (p == -1)
58 // the file is part of this directory
59 children.append(new Node(this,file,path));
61 else
63 QString subdir = path.left(p);
64 foreach (Node* n,children)
66 if (n->name == subdir)
68 n->insert(path.mid(p+1),file);
69 return;
73 Node* n = new Node(this,subdir);
74 children.append(n);
75 n->insert(path.mid(p+1),file);
79 int TorrentFileTreeModel::Node::row()
81 if (parent)
82 return parent->children.indexOf(this);
83 else
84 return 0;
87 bt::Uint64 TorrentFileTreeModel::Node::fileSize(const bt::TorrentInterface* tc)
89 if (size > 0)
90 return size;
92 if (!file)
94 // directory
95 foreach (Node* n,children)
96 size += n->fileSize(tc);
98 else
100 size = file->getSize();
102 return size;
105 bt::Uint64 TorrentFileTreeModel::Node::bytesToDownload(const bt::TorrentInterface* tc)
107 bt::Uint64 s = 0;
109 if (!file)
111 // directory
112 foreach (Node* n,children)
113 s += n->bytesToDownload(tc);
115 else
117 if (!file->doNotDownload())
118 s = file->getSize();
120 return s;
123 Qt::CheckState TorrentFileTreeModel::Node::checkState(const bt::TorrentInterface* tc) const
125 if (!file)
127 bool found_checked = false;
128 bool found_unchecked = false;
129 // directory
130 foreach (Node* n,children)
132 Qt::CheckState s = n->checkState(tc);
133 if (s == Qt::PartiallyChecked)
134 return s;
135 else if (s == Qt::Checked)
136 found_checked = true;
137 else
138 found_unchecked = true;
140 if (found_checked && found_unchecked)
141 return Qt::PartiallyChecked;
144 return found_checked ? Qt::Checked : Qt::Unchecked;
146 else
148 return file->doNotDownload() || file->getPriority() == ONLY_SEED_PRIORITY ? Qt::Unchecked : Qt::Checked;
152 void TorrentFileTreeModel::Node::saveExpandedState(const QModelIndex & index,QTreeView* tv,BEncoder* enc)
154 if (file)
155 return;
157 enc->write("expanded");
158 enc->write((Uint32)(tv->isExpanded(index) ? 1 : 0));
160 int idx = 0;
161 foreach (Node* n,children)
163 if (!n->file)
165 enc->write(n->name);
166 enc->beginDict();
167 n->saveExpandedState(index.child(idx,0),tv,enc);
168 enc->end();
170 idx++;
174 void TorrentFileTreeModel::Node::loadExpandedState(const QModelIndex & index,QTreeView* tv,BNode* n)
176 if (file)
177 return;
179 BDictNode* dict = dynamic_cast<BDictNode*>(n);
180 if (!dict)
181 return;
183 BValueNode* v = dict->getValue("expanded");
184 if (v)
185 tv->setExpanded(index,v->data().toInt() == 1);
187 int idx = 0;
188 foreach (Node* n,children)
190 if (!n->file)
192 BDictNode* d = dict->getDict(n->name);
193 if (d)
194 n->loadExpandedState(index.child(idx,0),tv,d);
196 idx++;
200 TorrentFileTreeModel::TorrentFileTreeModel(bt::TorrentInterface* tc,DeselectMode mode,QObject* parent)
201 : TorrentFileModel(tc,mode,parent),root(0),emit_check_state_change(true)
203 if (tc->getStats().multi_file_torrent)
204 constructTree();
205 else
206 root = new Node(0,tc->getStats().torrent_name);
210 TorrentFileTreeModel::~TorrentFileTreeModel()
213 void TorrentFileTreeModel::constructTree()
215 if (!root)
216 root = new Node(0,tc->getStats().torrent_name);
218 for (Uint32 i = 0;i < tc->getNumFiles();i++)
220 bt::TorrentFileInterface & tf = tc->getTorrentFile(i);
221 root->insert(tf.getPath(),&tf);
225 int TorrentFileTreeModel::rowCount(const QModelIndex & parent) const
227 if (!parent.isValid())
229 return 1;
231 else
233 Node* n = (Node*)parent.internalPointer();
234 return n->children.count();
238 int TorrentFileTreeModel::columnCount(const QModelIndex & parent) const
240 if (!parent.isValid())
241 return 2;
242 else
243 return 2;
246 QVariant TorrentFileTreeModel::headerData(int section, Qt::Orientation orientation,int role) const
248 if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
249 return QVariant();
251 switch (section)
253 case 0: return i18n("File");
254 case 1: return i18n("Size");
255 default:
256 return QVariant();
260 QVariant TorrentFileTreeModel::data(const QModelIndex & index, int role) const
262 if (!index.isValid())
263 return QVariant();
265 Node* n = (Node*)index.internalPointer();
266 if (!n)
267 return QVariant();
269 if (role == Qt::DisplayRole)
271 switch (index.column())
273 case 0: return n->name;
274 case 1:
275 if (tc->getStats().multi_file_torrent)
276 return BytesToString(n->fileSize(tc));
277 else
278 return BytesToString(tc->getStats().total_bytes);
279 default: return QVariant();
282 else if (role == Qt::DecorationRole && index.column() == 0)
284 // if this is an empty folder then we are in the single file case
285 if (!n->file)
286 return n->children.count() > 0 ?
287 KIcon("folder") : KIcon(KMimeType::findByPath(tc->getStats().torrent_name)->iconName());
288 else
289 return KIcon(KMimeType::findByPath(n->file->getPath())->iconName());
291 else if (role == Qt::CheckStateRole && index.column() == 0)
293 if (tc->getStats().multi_file_torrent)
294 return n->checkState(tc);
297 return QVariant();
300 QModelIndex TorrentFileTreeModel::parent(const QModelIndex & index) const
302 if (!index.isValid())
303 return QModelIndex();
305 Node* child = static_cast<Node*>(index.internalPointer());
306 if (!child)
307 return QModelIndex();
309 Node* parent = child->parent;
310 if (!parent)
311 return QModelIndex();
312 else
313 return createIndex(parent->row(), 0, parent);
316 QModelIndex TorrentFileTreeModel::index(int row,int column,const QModelIndex & parent) const
318 if (!hasIndex(row, column, parent))
319 return QModelIndex();
321 Node* p = 0;
323 if (!parent.isValid())
324 return createIndex(row,column,root);
325 else
327 p = static_cast<Node*>(parent.internalPointer());
329 if (row >= 0 && row < p->children.count())
330 return createIndex(row,column,p->children.at(row));
331 else
332 return QModelIndex();
336 Qt::ItemFlags TorrentFileTreeModel::flags(const QModelIndex & index) const
338 if (!index.isValid())
339 return 0;
340 else if (tc->getStats().multi_file_torrent)
341 return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
342 else
343 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
346 bool TorrentFileTreeModel::setData(const QModelIndex & index, const QVariant & value, int role)
348 if (!index.isValid() || role != Qt::CheckStateRole)
349 return false;
351 Node* n = static_cast<Node*>(index.internalPointer());
352 if (!n)
353 return false;
355 Qt::CheckState newState = static_cast<Qt::CheckState>(value.toInt());
356 if (!n->file)
358 bool reenable = false;
359 if (emit_check_state_change)
361 reenable = true;
362 emit_check_state_change = false;
365 for (Uint32 i = 0;i < n->children.count();i++)
367 // recurse down the tree
368 setData(index.child(i,0),value,role);
371 if (reenable)
372 emit_check_state_change = true;
374 else
376 bt::TorrentFileInterface* file = n->file;
377 if (newState == Qt::Checked)
379 if (file->getPriority() == ONLY_SEED_PRIORITY)
380 file->setPriority(NORMAL_PRIORITY);
381 else
382 file->setDoNotDownload(false);
384 else
386 if (mode == KEEP_FILES)
387 file->setPriority(ONLY_SEED_PRIORITY);
388 else
389 file->setDoNotDownload(true);
391 dataChanged(createIndex(index.row(),0),createIndex(index.row(),columnCount(index) - 1));
393 QModelIndex parent = index.parent();
394 if (parent.isValid())
395 dataChanged(parent,parent); // parent needs to be updated to
398 if (emit_check_state_change)
399 checkStateChanged();
400 return true;
403 void TorrentFileTreeModel::checkAll()
405 if (tc->getStats().multi_file_torrent)
406 setData(index(0,0,QModelIndex()),Qt::Checked,Qt::CheckStateRole);
409 void TorrentFileTreeModel::uncheckAll()
411 if (tc->getStats().multi_file_torrent)
412 setData(index(0,0,QModelIndex()),Qt::Unchecked,Qt::CheckStateRole);
415 void TorrentFileTreeModel::invertCheck()
417 if (!tc->getStats().multi_file_torrent)
418 return;
420 invertCheck(index(0,0,QModelIndex()));
423 void TorrentFileTreeModel::invertCheck(const QModelIndex & idx)
425 Node* n = static_cast<Node*>(idx.internalPointer());
426 if (!n)
427 return;
429 if (!n->file)
431 for (Uint32 i = 0;i < n->children.count();i++)
433 // recurse down the tree
434 invertCheck(idx.child(i,0));
437 else
439 if (n->file->doNotDownload())
440 setData(idx,Qt::Checked,Qt::CheckStateRole);
441 else
442 setData(idx,Qt::Unchecked,Qt::CheckStateRole);
446 bt::Uint64 TorrentFileTreeModel::bytesToDownload()
448 if (tc->getStats().multi_file_torrent)
449 return root->bytesToDownload(tc);
450 else
451 return tc->getStats().total_bytes;
454 QByteArray TorrentFileTreeModel::saveExpandedState(QTreeView* tv)
456 if (!tc->getStats().multi_file_torrent)
457 return QByteArray();
459 QByteArray data;
460 BEncoder enc(new BEncoderBufferOutput(data));
461 enc.beginDict();
462 root->saveExpandedState(index(0,0,QModelIndex()),tv,&enc);
463 enc.end();
464 return data;
468 void TorrentFileTreeModel::loadExpandedState(QTreeView* tv,const QByteArray & state)
470 if (!tc->getStats().multi_file_torrent)
471 return;
473 BDecoder dec(state,false,0);
474 BNode* n = dec.decode();
475 if (n && n->getType() == BNode::DICT)
477 n->printDebugInfo();
478 root->loadExpandedState(index(0,0,QModelIndex()),tv,n);
480 delete n;
483 bt::TorrentFileInterface* TorrentFileTreeModel::indexToFile(const QModelIndex & idx)
485 if (!idx.isValid())
486 return 0;
488 Node* n = (Node*)idx.internalPointer();
489 if (!n)
490 return 0;
492 return n->file;
495 QString TorrentFileTreeModel::dirPath(const QModelIndex & idx)
497 if (!idx.isValid())
498 return QString::null;
500 Node* n = (Node*)idx.internalPointer();
501 if (!n || n == root)
502 return QString::null;
504 QString ret = n->name;
507 n = n->parent;
508 if (n && n->parent)
509 ret = n->name + bt::DirSeparator() + ret;
510 }while (n);
512 return ret;
515 void TorrentFileTreeModel::changePriority(const QModelIndexList & indexes,bt::Priority newpriority)
517 foreach (QModelIndex idx,indexes)
519 Node* n = (Node*)idx.internalPointer();
520 if (!n)
521 continue;
523 setData(idx,newpriority,Qt::UserRole);
528 #include "torrentfiletreemodel.moc"