1 /***************************************************************************
2 * Copyright (C) 2007 by Joris Guisson and Ivan Vasic *
3 * joris.guisson@gmail.com *
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. *
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. *
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 ***************************************************************************/
23 #include <kmimetype.h>
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>
32 #include "torrentfiletreemodel.h"
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()
53 void TorrentFileTreeModel::Node::insert(const QString
& path
,bt::TorrentFileInterface
* file
)
55 int p
= path
.indexOf(bt::DirSeparator());
58 // the file is part of this directory
59 children
.append(new Node(this,file
,path
));
63 QString subdir
= path
.left(p
);
64 foreach (Node
* n
,children
)
66 if (n
->name
== subdir
)
68 n
->insert(path
.mid(p
+1),file
);
73 Node
* n
= new Node(this,subdir
);
75 n
->insert(path
.mid(p
+1),file
);
79 int TorrentFileTreeModel::Node::row()
82 return parent
->children
.indexOf(this);
87 bt::Uint64
TorrentFileTreeModel::Node::fileSize(const bt::TorrentInterface
* tc
)
95 foreach (Node
* n
,children
)
96 size
+= n
->fileSize(tc
);
100 size
= file
->getSize();
105 bt::Uint64
TorrentFileTreeModel::Node::bytesToDownload(const bt::TorrentInterface
* tc
)
112 foreach (Node
* n
,children
)
113 s
+= n
->bytesToDownload(tc
);
117 if (!file
->doNotDownload())
123 Qt::CheckState
TorrentFileTreeModel::Node::checkState(const bt::TorrentInterface
* tc
) const
127 bool found_checked
= false;
128 bool found_unchecked
= false;
130 foreach (Node
* n
,children
)
132 Qt::CheckState s
= n
->checkState(tc
);
133 if (s
== Qt::PartiallyChecked
)
135 else if (s
== Qt::Checked
)
136 found_checked
= true;
138 found_unchecked
= true;
140 if (found_checked
&& found_unchecked
)
141 return Qt::PartiallyChecked
;
144 return found_checked
? Qt::Checked
: Qt::Unchecked
;
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
)
157 enc
->write("expanded");
158 enc
->write((Uint32
)(tv
->isExpanded(index
) ? 1 : 0));
161 foreach (Node
* n
,children
)
167 n
->saveExpandedState(index
.child(idx
,0),tv
,enc
);
174 void TorrentFileTreeModel::Node::loadExpandedState(const QModelIndex
& index
,QTreeView
* tv
,BNode
* n
)
179 BDictNode
* dict
= dynamic_cast<BDictNode
*>(n
);
183 BValueNode
* v
= dict
->getValue("expanded");
185 tv
->setExpanded(index
,v
->data().toInt() == 1);
188 foreach (Node
* n
,children
)
192 BDictNode
* d
= dict
->getDict(n
->name
);
194 n
->loadExpandedState(index
.child(idx
,0),tv
,d
);
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
)
206 root
= new Node(0,tc
->getStats().torrent_name
);
210 TorrentFileTreeModel::~TorrentFileTreeModel()
213 void TorrentFileTreeModel::constructTree()
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())
233 Node
* n
= (Node
*)parent
.internalPointer();
234 return n
->children
.count();
238 int TorrentFileTreeModel::columnCount(const QModelIndex
& parent
) const
240 if (!parent
.isValid())
246 QVariant
TorrentFileTreeModel::headerData(int section
, Qt::Orientation orientation
,int role
) const
248 if (role
!= Qt::DisplayRole
|| orientation
!= Qt::Horizontal
)
253 case 0: return i18n("File");
254 case 1: return i18n("Size");
260 QVariant
TorrentFileTreeModel::data(const QModelIndex
& index
, int role
) const
262 if (!index
.isValid())
265 Node
* n
= (Node
*)index
.internalPointer();
269 if (role
== Qt::DisplayRole
)
271 switch (index
.column())
273 case 0: return n
->name
;
275 if (tc
->getStats().multi_file_torrent
)
276 return BytesToString(n
->fileSize(tc
));
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
286 return n
->children
.count() > 0 ?
287 KIcon("folder") : KIcon(KMimeType::findByPath(tc
->getStats().torrent_name
)->iconName());
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
);
300 QModelIndex
TorrentFileTreeModel::parent(const QModelIndex
& index
) const
302 if (!index
.isValid())
303 return QModelIndex();
305 Node
* child
= static_cast<Node
*>(index
.internalPointer());
307 return QModelIndex();
309 Node
* parent
= child
->parent
;
311 return QModelIndex();
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();
323 if (!parent
.isValid())
324 return createIndex(row
,column
,root
);
327 p
= static_cast<Node
*>(parent
.internalPointer());
329 if (row
>= 0 && row
< p
->children
.count())
330 return createIndex(row
,column
,p
->children
.at(row
));
332 return QModelIndex();
336 Qt::ItemFlags
TorrentFileTreeModel::flags(const QModelIndex
& index
) const
338 if (!index
.isValid())
340 else if (tc
->getStats().multi_file_torrent
)
341 return Qt::ItemIsSelectable
| Qt::ItemIsEnabled
| Qt::ItemIsUserCheckable
;
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
)
351 Node
* n
= static_cast<Node
*>(index
.internalPointer());
355 Qt::CheckState newState
= static_cast<Qt::CheckState
>(value
.toInt());
358 bool reenable
= false;
359 if (emit_check_state_change
)
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
);
372 emit_check_state_change
= true;
376 bt::TorrentFileInterface
* file
= n
->file
;
377 if (newState
== Qt::Checked
)
379 if (file
->getPriority() == ONLY_SEED_PRIORITY
)
380 file
->setPriority(NORMAL_PRIORITY
);
382 file
->setDoNotDownload(false);
386 if (mode
== KEEP_FILES
)
387 file
->setPriority(ONLY_SEED_PRIORITY
);
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
)
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
)
420 invertCheck(index(0,0,QModelIndex()));
423 void TorrentFileTreeModel::invertCheck(const QModelIndex
& idx
)
425 Node
* n
= static_cast<Node
*>(idx
.internalPointer());
431 for (Uint32 i
= 0;i
< n
->children
.count();i
++)
433 // recurse down the tree
434 invertCheck(idx
.child(i
,0));
439 if (n
->file
->doNotDownload())
440 setData(idx
,Qt::Checked
,Qt::CheckStateRole
);
442 setData(idx
,Qt::Unchecked
,Qt::CheckStateRole
);
446 bt::Uint64
TorrentFileTreeModel::bytesToDownload()
448 if (tc
->getStats().multi_file_torrent
)
449 return root
->bytesToDownload(tc
);
451 return tc
->getStats().total_bytes
;
454 QByteArray
TorrentFileTreeModel::saveExpandedState(QTreeView
* tv
)
456 if (!tc
->getStats().multi_file_torrent
)
460 BEncoder
enc(new BEncoderBufferOutput(data
));
462 root
->saveExpandedState(index(0,0,QModelIndex()),tv
,&enc
);
468 void TorrentFileTreeModel::loadExpandedState(QTreeView
* tv
,const QByteArray
& state
)
470 if (!tc
->getStats().multi_file_torrent
)
473 BDecoder
dec(state
,false,0);
474 BNode
* n
= dec
.decode();
475 if (n
&& n
->getType() == BNode::DICT
)
478 root
->loadExpandedState(index(0,0,QModelIndex()),tv
,n
);
483 bt::TorrentFileInterface
* TorrentFileTreeModel::indexToFile(const QModelIndex
& idx
)
488 Node
* n
= (Node
*)idx
.internalPointer();
495 QString
TorrentFileTreeModel::dirPath(const QModelIndex
& idx
)
498 return QString::null
;
500 Node
* n
= (Node
*)idx
.internalPointer();
502 return QString::null
;
504 QString ret
= n
->name
;
509 ret
= n
->name
+ bt::DirSeparator() + ret
;
515 void TorrentFileTreeModel::changePriority(const QModelIndexList
& indexes
,bt::Priority newpriority
)
517 foreach (QModelIndex idx
,indexes
)
519 Node
* n
= (Node
*)idx
.internalPointer();
523 setData(idx
,newpriority
,Qt::UserRole
);
528 #include "torrentfiletreemodel.moc"