1 /*
2  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
3  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
4  *
5  * Copyright (C) 2015  Ivan Romanov <drizt72@zoho.eu>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include "bencodemodel.h"
23 #include "bencode.h"
24 
25 #include <QTextCodec>
26 #include <QDateTime>
27 #include <QCryptographicHash>
28 #include <QStringList>
29 #include <QDebug>
30 #include <QUrl>
31 
BencodeModel(QObject * parent)32 BencodeModel::BencodeModel(QObject *parent)
33     : AbstractTreeModel(new Bencode(Bencode::Type::Dictionary), parent)
34     , _bencode(new Bencode(Bencode::Type::Dictionary, "root"))
35     , _originBencode(new Bencode(Bencode::Type::Dictionary, "root"))
36     , _textCodec(QTextCodec::codecForName("UTF-8"))
37 {
38     root()->appendChild(_bencode);
39 }
40 
~BencodeModel()41 BencodeModel::~BencodeModel()
42 {
43     delete _bencode;
44     delete _originBencode;
45 }
46 
setJson(const QVariant & json)47 void BencodeModel::setJson(const QVariant &json)
48 {
49     Bencode *newBencode = Bencode::fromJson(json);
50     if (newBencode && newBencode->compare(_bencode)) {
51         delete newBencode;
52         return;
53     }
54 
55     removeRows(0, rowCount());
56     _bencode = newBencode;
57     if (!_bencode)
58         _bencode = new Bencode(Bencode::Type::Dictionary);
59     _bencode->setKey("root");
60 
61     beginInsertRows(QModelIndex(), 0, 0);
62     root()->appendChild(_bencode);
63     endInsertRows();
64 }
65 
toJson() const66 QVariant BencodeModel::toJson() const
67 {
68     return _bencode ? _bencode->toJson() : QVariant();
69 }
70 
setRaw(const QByteArray & raw)71 void BencodeModel::setRaw(const QByteArray &raw)
72 {
73     Bencode *newBencode = Bencode::fromRaw(raw);
74     if (newBencode && newBencode->compare(_bencode)) {
75         delete newBencode;
76         return;
77     }
78 
79     removeRows(0, rowCount());
80     _bencode = newBencode;
81     if (!_bencode)
82         _bencode = new Bencode(Bencode::Type::Dictionary);
83     _bencode->setKey("root");
84 
85     beginInsertRows(QModelIndex(), 0, 0);
86     root()->appendChild(_bencode);
87     endInsertRows();
88 }
89 
toRaw() const90 QByteArray BencodeModel::toRaw() const
91 {
92     return _bencode->toRaw();
93 }
94 
isValid() const95 bool BencodeModel::isValid() const
96 {
97     return _bencode && _bencode->isValid();
98 }
99 
resetModified()100 void BencodeModel::resetModified()
101 {
102     delete _originBencode;
103     _originBencode = _bencode ? _bencode->clone() : nullptr;
104 }
105 
isModified() const106 bool BencodeModel::isModified() const
107 {
108     if (!_bencode && !_originBencode)
109         return false;
110 
111     if (!_bencode ^ !_originBencode)
112         return true;
113 
114     return _bencode ? !_bencode->compare(_originBencode) : false;
115 }
116 
setTextCodec(QTextCodec * textCodec)117 void BencodeModel::setTextCodec(QTextCodec *textCodec)
118 {
119     _textCodec = textCodec;
120     emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
121 }
122 
textCodec() const123 QTextCodec *BencodeModel::textCodec() const
124 {
125     return _textCodec;
126 }
127 
setName(const QString & name)128 void BencodeModel::setName(const QString &name)
129 {
130     if (name.isEmpty() && _bencode && _bencode->child("info") && _bencode->child("info")->child("name")) { // -V807 PVS-Studio
131         removeRow(_bencode->child("info")->child("name")->row(), nodeToIndex(_bencode->child("info")));
132         if (!_bencode->child("info")->childCount()) {
133             removeRow(_bencode->child("info")->row(), nodeToIndex(_bencode));
134         }
135     }
136     else if (!name.isEmpty()) {
137         emit layoutAboutToBeChanged();
138         _bencode->checkAndCreate(Bencode::Type::Dictionary, "info")->checkAndCreate(Bencode::Type::String, "name")->setString(fromUnicode(name));
139         emit layoutChanged();
140     }
141 }
142 
name() const143 QString BencodeModel::name() const
144 {
145     if (_bencode && _bencode->child("info") && _bencode->child("info")->child("name"))
146         return toUnicode(_bencode->child("info")->child("name")->string());
147     else
148         return QString();
149 }
150 
setPrivateTorrent(bool privateTorrent)151 void BencodeModel::setPrivateTorrent(bool privateTorrent)
152 {
153     if (!privateTorrent && _bencode && _bencode->child("info") && _bencode->child("info")->child("private")) { // -V807 PVS-Studio
154         removeRow(_bencode->child("info")->child("private")->row(), nodeToIndex(_bencode->child("info")));
155         if (!_bencode->child("info")->childCount()) {
156             removeRow(_bencode->child("info")->row(), nodeToIndex(_bencode));
157         }
158     }
159     else if (privateTorrent) {
160         emit layoutAboutToBeChanged();
161         _bencode->checkAndCreate(Bencode::Type::Dictionary, "info")->checkAndCreate(Bencode::Type::Integer, "private")->setInteger(1);
162         emit layoutChanged();
163     }
164 }
165 
privateTorrent() const166 bool BencodeModel::privateTorrent() const
167 {
168     if (_bencode && _bencode->child("info") && _bencode->child("info")->child("private"))
169         return static_cast<bool>(_bencode->child("info")->child("private")->integer());
170     else
171         return false;
172 }
173 
setUrl(const QString & url)174 void BencodeModel::setUrl(const QString &url)
175 {
176     if (url.isEmpty() && _bencode && _bencode->child("publisher-url")) {
177         removeRow(_bencode->child("publisher-url")->row(), nodeToIndex(_bencode));
178     }
179     else if (!url.isEmpty()) {
180         emit layoutAboutToBeChanged();
181         _bencode->checkAndCreate(Bencode::Type::String, "publisher-url")->setString(fromUnicode(url));
182         emit layoutChanged();
183     }
184 }
185 
url() const186 QString BencodeModel::url() const
187 {
188     if (_bencode && _bencode->child("publisher-url"))
189         return toUnicode(_bencode->child("publisher-url")->string());
190     else
191         return QString();
192 }
193 
setPublisher(const QString & publisher)194 void BencodeModel::setPublisher(const QString &publisher)
195 {
196     if (publisher.isEmpty() && _bencode && _bencode->child("publisher")) {
197         removeRow(_bencode->child("publisher")->row(), nodeToIndex(_bencode));
198     }
199     else if (!publisher.isEmpty()) {
200         emit layoutAboutToBeChanged();
201         _bencode->checkAndCreate(Bencode::Type::String, "publisher")->setString(fromUnicode(publisher));
202         emit layoutChanged();
203     }
204 }
205 
publisher() const206 QString BencodeModel::publisher() const
207 {
208     if (_bencode && _bencode->child("publisher"))
209         return toUnicode(_bencode->child("publisher")->string());
210     else
211         return QString();
212 }
213 
setCreatedBy(const QString & createdBy)214 void BencodeModel::setCreatedBy(const QString &createdBy)
215 {
216     if (createdBy.isEmpty() && _bencode && _bencode->child("created by")) {
217         removeRow(_bencode->child("created by")->row(), nodeToIndex(_bencode));
218     }
219     else if (!createdBy.isEmpty()) {
220         emit layoutAboutToBeChanged();
221         _bencode->checkAndCreate(Bencode::Type::String, "created by")->setString(fromUnicode(createdBy));
222         emit layoutChanged();
223     }
224 }
225 
createdBy() const226 QString BencodeModel::createdBy() const
227 {
228     if (_bencode && _bencode->child("created by"))
229         return toUnicode(_bencode->child("created by")->string());
230     else
231         return QString();
232 }
233 
setCreationTime(const QDateTime & creationTime)234 void BencodeModel::setCreationTime(const QDateTime &creationTime)
235 {
236     if (!creationTime.isValid() && _bencode && _bencode->child("creation date")) {
237         removeRow(_bencode->child("creation date")->row(), nodeToIndex(_bencode));
238     }
239     else if (creationTime.isValid()) {
240         emit layoutAboutToBeChanged();
241         _bencode->checkAndCreate(Bencode::Type::Integer, "creation date")->setInteger(static_cast<qlonglong>(creationTime.toMSecsSinceEpoch() / 1000));
242         emit layoutChanged();
243     }
244 }
245 
creationTime() const246 QDateTime BencodeModel::creationTime() const
247 {
248     QDateTime dateTime;
249     if (_bencode && _bencode->child("creation date"))
250         dateTime = QDateTime::fromMSecsSinceEpoch(_bencode->child("creation date")->integer() * 1000);
251     return dateTime;
252 }
253 
setPieceSize(int pieceSize)254 void BencodeModel::setPieceSize(int pieceSize)
255 {
256     if (!pieceSize && _bencode && _bencode->child("info") && _bencode->child("info")->child("piece length")) { // -V807 PVS-Studio
257         removeRow(_bencode->child("info")->child("piece length")->row(), nodeToIndex(_bencode->child("info")));
258         if (!_bencode->child("info")->childCount()) {
259             removeRow(_bencode->child("info")->row(), nodeToIndex(_bencode));
260         }
261     }
262     else if (pieceSize) {
263         emit layoutAboutToBeChanged();
264         _bencode->checkAndCreate(Bencode::Type::Dictionary, "info")->checkAndCreate(Bencode::Type::Integer, "piece length")->setInteger(pieceSize);
265         emit layoutChanged();
266     }
267 }
268 
pieceSize() const269 int BencodeModel::pieceSize() const
270 {
271     if (_bencode && _bencode->child("info") && _bencode->child("info")->child("piece length"))
272         return _bencode->child("info")->child("piece length")->integer();
273     else
274         return 0;
275 }
276 
pieces() const277 int BencodeModel::pieces() const
278 {
279     if (_bencode && _bencode->child("info") && _bencode->child("info")->child("pieces"))
280         return _bencode->child("info")->child("pieces")->string().size() / 20;
281     else
282         return 0;
283 }
284 
hash() const285 QString BencodeModel::hash() const
286 {
287     QByteArray hash;
288     if (_bencode && _bencode->child("info"))
289         hash = QCryptographicHash::hash(_bencode->child("info")->toRaw(), QCryptographicHash::Sha1).toHex();
290     return QString::fromUtf8(hash);
291 }
292 
magnetLink() const293 QString BencodeModel::magnetLink() const
294 {
295     QByteArray link;
296 
297     if (!hash().isEmpty()) {
298         link = "magnet:?xt=urn:btih:" + hash().toUtf8();
299         if (!name().isEmpty()) {
300             link += "&dn=" + QUrl::toPercentEncoding(name());
301         }
302 
303         for (const QString &tracker: trackers()) {
304             link += "&tr=" + QUrl::toPercentEncoding(tracker);
305         }
306     }
307 
308     return QString::fromUtf8(link);
309 }
310 
setComment(const QString & comment)311 void BencodeModel::setComment(const QString &comment)
312 {
313     if (comment.isEmpty() && _bencode && _bencode->child("comment")) {
314         removeRow(_bencode->child("comment")->row(), nodeToIndex(_bencode));
315     }
316     else if (!comment.isEmpty()) {
317         emit layoutAboutToBeChanged();
318         _bencode->checkAndCreate(Bencode::Type::String, "comment")->setString(fromUnicode(comment));
319         emit layoutChanged();
320     }
321 }
322 
comment() const323 QString BencodeModel::comment() const
324 {
325     if (_bencode && _bencode->child("comment"))
326         return toUnicode(_bencode->child("comment")->string());
327     else
328         return QString();
329 }
330 
setTrackers(const QStringList & trackers)331 void BencodeModel::setTrackers(const QStringList &trackers)
332 {
333     if (_bencode->child("announce-list"))
334         removeRow(_bencode->child("announce-list")->row(), nodeToIndex(_bencode));
335 
336     emit layoutAboutToBeChanged();
337     _bencode->appendMapItem(new Bencode(Bencode::Type::List, "announce-list"));
338 
339     for (const QString &tracker: trackers) {
340         if (tracker.trimmed().isEmpty())
341             continue;
342 
343         Bencode *item = new Bencode(fromUnicode(tracker));
344         Bencode *parentItem = new Bencode(Bencode::Type::List);
345         parentItem->appendChild(item);
346         _bencode->child("announce-list")->appendChild(parentItem); // -V595 // -V807 PVS-Studio
347     }
348 
349     if (_bencode->child("announce-list") && !_bencode->child("announce-list")->children().isEmpty()) {
350         _bencode->checkAndCreate(Bencode::Type::String, "announce")->setString(_bencode->child("announce-list")->child(0)->child(0)->string());
351     }
352     else {
353         delete _bencode->child("announce-list");
354         delete _bencode->child("announce");
355     }
356     emit layoutChanged();
357 }
358 
trackers() const359 QStringList BencodeModel::trackers() const
360 {
361     QStringList trackers;
362     Bencode *list = _bencode ? _bencode->child("announce-list") : nullptr;
363 
364     if (list) {
365         for (int i = 0; i < list->childCount(); i++) {
366             Bencode *bencode = list->child(i);
367             if (bencode->isList() && bencode->childCount() == 1 && bencode->child(0)->isString())
368                 trackers << toUnicode(bencode->child(0)->string());
369         }
370     }
371 
372     if (trackers.isEmpty()) {
373         if (_bencode->child("announce") &&  _bencode->child("announce")->isString())
374             trackers << toUnicode(_bencode->child("announce")->string());
375     }
376 
377     return trackers;
378 }
379 
setFiles(const QList<QPair<QString,qlonglong>> & files)380 void BencodeModel::setFiles(const QList<QPair<QString, qlonglong>> &files)
381 {
382     emit layoutAboutToBeChanged();
383 
384     if (files.size() == 1) {
385         qlonglong totalSize = files.first().second;
386         _bencode->child("info")->checkAndCreate(Bencode::Type::Integer, "length")->setInteger(totalSize);
387     }
388     else {
389         delete _bencode->child("info")->child("files");
390         _bencode->child("info")->appendMapItem(new Bencode(Bencode::Type::List, "files"));
391         for (const auto &filePair: files) {
392             QString file = filePair.first;
393             qlonglong size = filePair.second;
394 
395             Bencode *fileItem = new Bencode(Bencode::Type::Dictionary);
396             fileItem->appendMapItem(new Bencode(size, "length"));
397 
398             QStringList pathList = file.split(QStringLiteral("/"));
399             fileItem->appendMapItem(new Bencode(Bencode::Type::List, "path"));
400             for (const QString &path: pathList) {
401                 fileItem->child("path")->appendChild(new Bencode(fromUnicode(path)));
402             }
403             _bencode->child("info")->child("files")->appendChild(fileItem);
404         }
405     }
406 
407     emit layoutChanged();
408 }
409 
files() const410 QList<QPair<QString, qlonglong>> BencodeModel::files() const
411 {
412     QList<QPair<QString, qlonglong>> res;
413 
414     Bencode *info = _bencode->child("info");
415     if (!info)
416         return res;
417 
418     // Torrent contains only one file
419     if (!info->child("files")) {
420         QString baseName;
421         if (info->child("name") && info->child("name")->isString()) {
422             baseName = toUnicode(info->child("name")->string());
423             qlonglong length = 0;
424             if (info->child("length") && info->child("length")->isInteger()) {
425                 length = info->child("length")->integer();
426             }
427             res << QPair<QString, qlonglong>(baseName, length);
428         }
429     }
430     else {
431         Bencode *list = info->child("files");
432         if (!list)
433             return res;
434 
435         for (int i = 0; i < list->childCount(); i++) {
436             Bencode *item = list->child(i);
437             QStringList path;
438             Bencode *pathList = item->child("path");
439             if (!pathList)
440                 continue;
441 
442             for (int i = 0; i < pathList->childCount(); i++) {
443                 path << toUnicode(pathList->child(i)->string());
444             }
445 
446             res << QPair<QString, qlonglong>(path.join(QStringLiteral("/")), item->child("length")->integer());
447         }
448     }
449 
450     return res;
451 }
452 
totalSize() const453 qulonglong BencodeModel::totalSize() const
454 {
455     qulonglong res = 0;
456 
457     Bencode *info = _bencode->child("info");
458     if (!info) {
459         return res;
460     }
461 
462     // Torrent contains only one file
463     if (!info->child("files")) {
464         QString baseName;
465         if (info->child("name") && info->child("name")->isString()) {
466             qlonglong length = 0;
467             if (info->child("length") && info->child("length")->isInteger()) {
468                 length = info->child("length")->integer();
469             }
470             res += length;
471         }
472     }
473     else {
474         Bencode *list = info->child("files");
475         if (!list) {
476             return res;
477         }
478 
479         for (int i = 0; i < list->childCount(); i++) {
480             Bencode *item = list->child(i);
481             QStringList path;
482             Bencode *pathList = item->child("path");
483             if (!pathList) {
484                 continue;
485             }
486 
487             res += item->child("length")->integer();
488         }
489     }
490 
491     return res;
492 }
493 
setPieces(const QByteArray & pieces)494 void BencodeModel::setPieces(const QByteArray &pieces)
495 {
496     if (!pieces.isEmpty()) {
497         emit layoutAboutToBeChanged();
498         if (!_bencode->child("info"))
499             _bencode->appendMapItem(new Bencode(Bencode::Type::Dictionary, "info"));
500 
501         if (!_bencode->child("info")->child("pieces"))
502             _bencode->child("info")->appendMapItem(new Bencode("", "pieces"));
503 
504         _bencode->child("info")->child("pieces")->setString(pieces);
505         _bencode->child("info")->child("pieces")->setHex(true);
506         emit layoutChanged();
507     }
508     else {
509         removeRows(0, rowCount());
510         _bencode = new Bencode(Bencode::Type::Dictionary, "root");
511         beginInsertRows(QModelIndex(), 0, 0);
512         root()->appendChild(_bencode);
513         endInsertRows();
514     }
515 }
516 
up(const QModelIndex & index)517 void BencodeModel::up(const QModelIndex &index)
518 {
519     Bencode *item = indexToNode(index);
520     if (!item || root() == item)
521         return;
522 
523     if (index.row() == 0)
524         return;
525 
526     if (!item->parent()->isList())
527         return;
528 
529     beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), index.row() - 1);
530     item->setRow(index.row() - 1);
531     endMoveRows();
532 }
533 
down(const QModelIndex & index)534 void BencodeModel::down(const QModelIndex &index)
535 {
536     Bencode *item = indexToNode(index);
537     if (!item || root() == item)
538         return;
539 
540     if (index.row() + 1 == rowCount(index.parent()))
541         return;
542 
543     if (!item->parent()->isList())
544         return;
545 
546     beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), index.row() + 2);
547     item->setRow(index.row() + 1);
548     endMoveRows();
549 }
550 
appendRow(const QModelIndex & parent)551 void BencodeModel::appendRow(const QModelIndex &parent)
552 {
553     Bencode *parentItem = indexToNode(parent);
554     if (!parentItem)
555         return;
556     QModelIndex parentIndex = parent;
557     if (!parentItem->isList() && !parentItem->isDictionary()) {
558         parentItem = parentItem->parent();
559         parentIndex = parent.parent();
560     }
561 
562     if (parentItem->isList()) {
563         insertRow(rowCount(parentIndex), parentIndex);
564     }
565     else if (parentItem->isDictionary()) {
566         insertRow(0, parentIndex);
567     }
568 }
569 
changeType(const QModelIndex & index,Bencode::Type type)570 void BencodeModel::changeType(const QModelIndex &index, Bencode::Type type)
571 {
572     Bencode *bencode = indexToNode(index);
573     if (!bencode || bencode->type() == type)
574         return;
575 
576     emit layoutAboutToBeChanged();
577     bencode->setType(type);
578     emit layoutChanged();
579 }
580 
columnCount(const QModelIndex & parent) const581 int BencodeModel::columnCount(const QModelIndex &parent) const
582 {
583     Q_UNUSED(parent)
584     return static_cast<int>(Column::Count);
585 }
586 
setData(const QModelIndex & index,const QVariant & value,int role)587 bool BencodeModel::setData(const QModelIndex &index, const QVariant &value, int role)
588 {
589     if (!index.isValid())
590         return false;
591 
592     if (role != Qt::EditRole && role != Qt::CheckStateRole && role < Qt::UserRole)
593         return false;
594 
595     Bencode *item = indexToNode(index);
596     Column column = static_cast<Column>(index.column());
597 
598     bool res = false;
599 
600     switch (role) {
601     case Qt::EditRole:
602         if (column == Column::Name) {
603             QByteArray newKey = fromUnicode(value.toString());
604             Bencode *parentItem = item->parent();
605             int newRow;
606             for (newRow = 0; newRow < parentItem->childCount(); newRow++) {
607 
608                 if (newKey < parentItem->child(newRow)->key()) {
609                     break;
610                 }
611             }
612 
613             // Fixed new item pos when going to right
614             int realRow = newRow > item->row() ? newRow - 1 : newRow;
615 
616             item->setKey(newKey);
617             res = true;
618 
619             if (realRow == item->row()) {
620                 emit dataChanged(index, index);
621             }
622             else {
623                 beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), newRow);
624                 item->setRow(realRow);
625                 endMoveRows();
626             }
627         }
628         else if (column == Column::Value) {
629             if (item->isInteger()) {
630                 item->setInteger(value.toLongLong());
631             }
632             else if (item->isString()) {
633                 if (item->hex())
634                     item->setString(QByteArray::fromHex(value.toByteArray()));
635                 else
636                     item->setString(fromUnicode(value.toString()));
637             }
638             res = true;
639             emit dataChanged(index, index);
640         }
641         break;
642 
643     case Qt::CheckStateRole:
644         if (column == Column::Hex) {
645             res = true;
646             item->setHex(value.toBool());
647             emit dataChanged(index, index.sibling(index.row(), static_cast<int>(Column::Value)));
648         }
649 
650         break;
651 
652     case Qt::UserRole:
653     case Qt::UserRole + 1:
654         if (item->isInteger()) {
655             item->setInteger(value.toLongLong());
656         }
657         else if (item->isString()) {
658             if (role == Qt::UserRole) {
659                 item->setString(fromUnicode(value.toString()));
660             }
661             else {
662                 item->setString(QByteArray::fromHex(value.toByteArray()));
663             }
664         }
665         res = true;
666         emit dataChanged(index.sibling(index.row(), static_cast<int>(Column::Value)), index.sibling(index.row(), static_cast<int>(Column::Value)));
667         break;
668 
669     default:
670         break;
671     }
672 
673     return res;
674 }
675 
data(const QModelIndex & index,int role) const676 QVariant BencodeModel::data(const QModelIndex &index, int role) const
677 {
678     if (!index.isValid())
679         return QVariant();
680 
681     QVariant res;
682     Column column = static_cast<Column>(index.column());
683     Bencode *item = indexToNode(index);
684     if (role == Qt::DisplayRole || role == Qt::EditRole) {
685         switch (column) {
686         case Column::Name:
687             if (item->parent()->isDictionary())
688                 res = toUnicode(item->key());
689             else
690                 res = item->row();
691             break;
692 
693         case Column::Type:
694             if (role == Qt::DisplayRole)
695                 res = Bencode::typeToStr(item->type());
696             else
697                 res = static_cast<int>(item->type());
698             break;
699 
700         case Column::Value:
701             if (item->isInteger()) {
702                 res = item->integer();
703             }
704             else if (item->isString()) {
705                 if (role == Qt::DisplayRole) {
706                     if (item->hex())
707                         res = QString::fromUtf8(item->string().toHex()).left(150);
708                     else
709                         res = toUnicode(item->string()).left(150);
710                 }
711                 else {
712                     if (item->hex())
713                         res = QString::fromUtf8(item->string().toHex());
714                     else
715                         res = toUnicode(item->string());
716                 }
717             }
718             break;
719 
720         default:
721             break;
722         }
723     }
724     else if (role == Qt::CheckStateRole) {
725         if (column == Column::Hex && item->isString()) {
726             res = item->hex() ? Qt::Checked : Qt::Unchecked;
727         }
728     }
729     else if (role >= Qt::UserRole) {
730         if (item->isInteger()) {
731             res = QString::number(item->integer());
732         }
733         else if (item->isString()) {
734             if (role == Qt::ItemDataRole::UserRole) {
735                 res = toUnicode(item->string());
736             }
737             else {
738                 res = QString::fromUtf8(item->string().toHex());
739             }
740         }
741 
742     }
743 
744     return res;
745 }
746 
headerData(int section,Qt::Orientation orientation,int role) const747 QVariant BencodeModel::headerData(int section, Qt::Orientation orientation, int role) const
748 {
749     QVariant res = QVariant();
750 
751     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
752         Column column = static_cast<Column>(section);
753         switch (column) {
754             case Column::Name:  res = QVariant(tr("Name"));   break;
755             case Column::Type:  res = QVariant(tr("Type"));   break;
756             case Column::Hex:   res = QVariant(tr("Hex"));    break;
757             case Column::Value: res = QVariant(tr("Value"));  break;
758             default: break;
759         }
760     }
761     return res;
762 }
763 
insertRows(int row,int count,const QModelIndex & parent)764 bool BencodeModel::insertRows(int row, int count, const QModelIndex &parent)
765 {
766     Bencode *bencodeParent = indexToNode(parent);
767 
768     if (row > bencodeParent->childCount())
769         return false;
770 
771     beginInsertRows(parent, row, row + count - 1);
772     for (int i = 0; i < count; i++) {
773         bencodeParent->insertChild(row, new Bencode(0));
774     }
775     endInsertRows();
776     return true;
777 }
778 
removeRows(int row,int count,const QModelIndex & parent)779 bool BencodeModel::removeRows(int row, int count, const QModelIndex &parent)
780 {
781     Bencode *bencodeParent = indexToNode(parent);
782 
783     if (bencodeParent->children().size() < row + count)
784         return false;
785 
786     beginRemoveRows(parent, row, row + count - 1);
787     for (int i = 0; i < count; i++) {
788         Bencode *item = bencodeParent->child(row);
789         if (item == _bencode) {
790             _bencode = nullptr;
791         }
792         delete item;
793     }
794     endRemoveRows();
795 
796     return true;
797 }
798 
flags(const QModelIndex & index) const799 Qt::ItemFlags BencodeModel::flags(const QModelIndex &index) const
800 {
801     if (!index.isValid())
802         return AbstractTreeModel::flags(index);
803 
804     Column column = static_cast<Column>(index.column());
805     Bencode *item = indexToNode(index);
806 
807     Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
808     if (item == _bencode)
809         return f;
810 
811     switch (column){
812     case Column::Name:
813         if (item->parent()->isDictionary())
814             f |= Qt::ItemIsEditable;
815         break;
816 
817     case Column::Type:
818         f |= Qt::ItemIsEditable;
819         break;
820 
821     case Column::Hex:
822         f |= Qt::ItemIsUserCheckable;
823         break;
824 
825     case Column::Value:
826         if (item->isInteger() || item->isString())
827             f |= Qt::ItemIsEditable;
828         break;
829 
830     default:
831         break;
832     }
833     return f;
834 }
835 
toUnicode(const QByteArray & encoded) const836 QString BencodeModel::toUnicode(const QByteArray &encoded) const
837 {
838     return _textCodec->toUnicode(encoded);
839 }
840 
fromUnicode(const QString & unicode) const841 QByteArray BencodeModel::fromUnicode(const QString &unicode) const
842 {
843     return _textCodec->fromUnicode(unicode);
844 }
845