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