mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-20 13:23:34 -07:00
Merge pull request #4936 from evsh/files-tooltip
Improve tooltips for torrent progress bar
This commit is contained in:
commit
95fbff3a53
17 changed files with 1521 additions and 1010 deletions
|
@ -46,6 +46,7 @@ utils/misc.h
|
||||||
utils/string.h
|
utils/string.h
|
||||||
filesystemwatcher.h
|
filesystemwatcher.h
|
||||||
iconprovider.h
|
iconprovider.h
|
||||||
|
indexrange.h
|
||||||
logger.h
|
logger.h
|
||||||
preferences.h
|
preferences.h
|
||||||
qinisettings.h
|
qinisettings.h
|
||||||
|
|
|
@ -6,6 +6,7 @@ HEADERS += \
|
||||||
$$PWD/logger.h \
|
$$PWD/logger.h \
|
||||||
$$PWD/settingsstorage.h \
|
$$PWD/settingsstorage.h \
|
||||||
$$PWD/preferences.h \
|
$$PWD/preferences.h \
|
||||||
|
$$PWD/indexrange.h \
|
||||||
$$PWD/iconprovider.h \
|
$$PWD/iconprovider.h \
|
||||||
$$PWD/http/irequesthandler.h \
|
$$PWD/http/irequesthandler.h \
|
||||||
$$PWD/http/connection.h \
|
$$PWD/http/connection.h \
|
||||||
|
|
|
@ -597,25 +597,6 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<int, int> TorrentHandle::fileExtremityPieces(int index) const
|
|
||||||
{
|
|
||||||
if (!hasMetadata()) return qMakePair(-1, -1);
|
|
||||||
|
|
||||||
const int numPieces = piecesCount();
|
|
||||||
const qlonglong pieceSize = pieceLength();
|
|
||||||
|
|
||||||
// Determine the first and last piece of the file
|
|
||||||
int firstPiece = floor((m_torrentInfo.fileOffset(index) + 1) / (float) pieceSize);
|
|
||||||
Q_ASSERT((firstPiece >= 0) && (firstPiece < numPieces));
|
|
||||||
|
|
||||||
int numPiecesInFile = ceil(fileSize(index) / (float) pieceSize);
|
|
||||||
int lastPiece = firstPiece + numPiecesInFile - 1;
|
|
||||||
Q_ASSERT((lastPiece >= 0) && (lastPiece < numPieces));
|
|
||||||
|
|
||||||
Q_UNUSED(numPieces)
|
|
||||||
return qMakePair(firstPiece, lastPiece);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> TorrentHandle::filePriorities() const
|
QVector<int> TorrentHandle::filePriorities() const
|
||||||
{
|
{
|
||||||
std::vector<int> fp;
|
std::vector<int> fp;
|
||||||
|
@ -733,13 +714,13 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
|
||||||
std::vector<int> fp;
|
std::vector<int> fp;
|
||||||
SAFE_GET(fp, file_priorities);
|
SAFE_GET(fp, file_priorities);
|
||||||
|
|
||||||
QPair<int, int> extremities;
|
TorrentInfo::PieceRange extremities;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
int count = static_cast<int>(fp.size());
|
int count = static_cast<int>(fp.size());
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const QString ext = Utils::Fs::fileExtension(filePath(i));
|
const QString ext = Utils::Fs::fileExtension(filePath(i));
|
||||||
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
|
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
|
||||||
extremities = fileExtremityPieces(i);
|
extremities = info().filePieces(i);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -749,8 +730,8 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
|
||||||
|
|
||||||
int first = 0;
|
int first = 0;
|
||||||
int last = 0;
|
int last = 0;
|
||||||
SAFE_GET(first, piece_priority, extremities.first);
|
SAFE_GET(first, piece_priority, extremities.first());
|
||||||
SAFE_GET(last, piece_priority, extremities.second);
|
SAFE_GET(last, piece_priority, extremities.last());
|
||||||
|
|
||||||
return ((first == 7) && (last == 7));
|
return ((first == 7) && (last == 7));
|
||||||
}
|
}
|
||||||
|
@ -1245,13 +1226,13 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
|
||||||
// Determine the priority to set
|
// Determine the priority to set
|
||||||
int prio = b ? 7 : fp[index];
|
int prio = b ? 7 : fp[index];
|
||||||
|
|
||||||
QPair<int, int> extremities = fileExtremityPieces(index);
|
TorrentInfo::PieceRange extremities = info().filePieces(index);
|
||||||
|
|
||||||
// worst case: AVI index = 1% of total file size (at the end of the file)
|
// worst case: AVI index = 1% of total file size (at the end of the file)
|
||||||
int nNumPieces = ceil(fileSize(index) * 0.01 / pieceLength());
|
int nNumPieces = ceil(fileSize(index) * 0.01 / pieceLength());
|
||||||
for (int i = 0; i < nNumPieces; ++i) {
|
for (int i = 0; i < nNumPieces; ++i) {
|
||||||
pp[extremities.first + i] = prio;
|
pp[extremities.first() + i] = prio;
|
||||||
pp[extremities.second - i] = prio;
|
pp[extremities.last() - i] = prio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,6 @@ namespace BitTorrent
|
||||||
qlonglong fileSize(int index) const;
|
qlonglong fileSize(int index) const;
|
||||||
QStringList absoluteFilePaths() const;
|
QStringList absoluteFilePaths() const;
|
||||||
QStringList absoluteFilePathsUnwanted() const;
|
QStringList absoluteFilePathsUnwanted() const;
|
||||||
QPair<int, int> fileExtremityPieces(int index) const;
|
|
||||||
QVector<int> filePriorities() const;
|
QVector<int> filePriorities() const;
|
||||||
|
|
||||||
TorrentInfo info() const;
|
TorrentInfo info() const;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
@ -138,6 +139,12 @@ int TorrentInfo::pieceLength() const
|
||||||
return m_nativeInfo->piece_length();
|
return m_nativeInfo->piece_length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TorrentInfo::pieceLength(int index) const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->piece_size(index);
|
||||||
|
}
|
||||||
|
|
||||||
int TorrentInfo::piecesCount() const
|
int TorrentInfo::piecesCount() const
|
||||||
{
|
{
|
||||||
if (!isValid()) return -1;
|
if (!isValid()) return -1;
|
||||||
|
@ -213,24 +220,79 @@ QByteArray TorrentInfo::metadata() const
|
||||||
|
|
||||||
QStringList TorrentInfo::filesForPiece(int pieceIndex) const
|
QStringList TorrentInfo::filesForPiece(int pieceIndex) const
|
||||||
{
|
{
|
||||||
if (pieceIndex < 0)
|
// no checks here because fileIndicesForPiece() will return an empty list
|
||||||
return QStringList();
|
QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||||
|
|
||||||
std::vector<libtorrent::file_slice> files(
|
|
||||||
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
|
|
||||||
QStringList res;
|
QStringList res;
|
||||||
for (const libtorrent::file_slice& s: files) {
|
res.reserve(fileIndices.size());
|
||||||
res.append(filePath(s.file_index));
|
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
|
||||||
}
|
[this](int i) { return filePath(i); });
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<int> TorrentInfo::fileIndicesForPiece(int pieceIndex) const
|
||||||
|
{
|
||||||
|
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
|
||||||
|
return QVector<int>();
|
||||||
|
|
||||||
|
std::vector<libt::file_slice> files(
|
||||||
|
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
|
||||||
|
QVector<int> res;
|
||||||
|
res.reserve(files.size());
|
||||||
|
std::transform(files.begin(), files.end(), std::back_inserter(res),
|
||||||
|
[](const libt::file_slice &s) { return s.file_index; });
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
|
||||||
|
{
|
||||||
|
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int index = fileIndex(file);
|
||||||
|
if (index == -1) {
|
||||||
|
qDebug() << "Filename" << file << "was not found in torrent" << name();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return filePieces(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
|
||||||
|
{
|
||||||
|
if (!isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if ((fileIndex < 0) || (fileIndex >= filesCount())) {
|
||||||
|
qDebug() << "File index (" << fileIndex << ") is out of range for torrent" << name();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const libt::file_storage &files = nativeInfo()->files();
|
||||||
|
const auto fileSize = files.file_size(fileIndex);
|
||||||
|
const auto firstOffset = files.file_offset(fileIndex);
|
||||||
|
return makeInterval(static_cast<int>(firstOffset / pieceLength()),
|
||||||
|
static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
||||||
{
|
{
|
||||||
if (!isValid()) return;
|
if (!isValid()) return;
|
||||||
nativeInfo()->rename_file(index, Utils::String::toStdString(newPath));
|
nativeInfo()->rename_file(index, Utils::String::toStdString(newPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
||||||
|
{
|
||||||
|
// the check whether the object valid is not needed here
|
||||||
|
// because filesCount() returns -1 in that case and the loop exits immediately
|
||||||
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
if (fileName == filePath(i))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
|
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
|
||||||
{
|
{
|
||||||
return m_nativeInfo;
|
return m_nativeInfo;
|
||||||
|
|
|
@ -34,12 +34,15 @@
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#include "base/indexrange.h"
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
class QStringList;
|
class QStringList;
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
template<typename T> class QList;
|
template<typename T> class QList;
|
||||||
|
template<typename T> class QVector;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
|
@ -75,6 +78,7 @@ namespace BitTorrent
|
||||||
qlonglong totalSize() const;
|
qlonglong totalSize() const;
|
||||||
int filesCount() const;
|
int filesCount() const;
|
||||||
int pieceLength() const;
|
int pieceLength() const;
|
||||||
|
int pieceLength(int index) const;
|
||||||
int piecesCount() const;
|
int piecesCount() const;
|
||||||
QString filePath(int index) const;
|
QString filePath(int index) const;
|
||||||
QStringList filePaths() const;
|
QStringList filePaths() const;
|
||||||
|
@ -86,12 +90,21 @@ namespace BitTorrent
|
||||||
QList<QUrl> urlSeeds() const;
|
QList<QUrl> urlSeeds() const;
|
||||||
QByteArray metadata() const;
|
QByteArray metadata() const;
|
||||||
QStringList filesForPiece(int pieceIndex) const;
|
QStringList filesForPiece(int pieceIndex) const;
|
||||||
|
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
||||||
|
|
||||||
|
using PieceRange = IndexRange<int>;
|
||||||
|
// returns pair of the first and the last pieces into which
|
||||||
|
// the given file extends (maybe partially).
|
||||||
|
PieceRange filePieces(const QString &file) const;
|
||||||
|
PieceRange filePieces(int fileIndex) const;
|
||||||
|
|
||||||
void renameFile(uint index, const QString &newPath);
|
void renameFile(uint index, const QString &newPath);
|
||||||
|
|
||||||
NativePtr nativeInfo() const;
|
NativePtr nativeInfo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// returns file index or -1 if fileName is not found
|
||||||
|
int fileIndex(const QString &fileName) const;
|
||||||
NativePtr m_nativeInfo;
|
NativePtr m_nativeInfo;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
130
src/base/indexrange.h
Normal file
130
src/base/indexrange.h
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2016 Eugene Shalygin
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QBT_INDEXRANGE_H
|
||||||
|
#define QBT_INDEXRANGE_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
// Interval is defined via [first;last]
|
||||||
|
template <typename Index>
|
||||||
|
class IndexInterval
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using IndexType = Index;
|
||||||
|
|
||||||
|
IndexInterval(IndexType first, IndexType last)
|
||||||
|
: m_first {first}
|
||||||
|
, m_last {last}
|
||||||
|
{
|
||||||
|
Q_ASSERT(first <= last);
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexType first() const
|
||||||
|
{
|
||||||
|
return m_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexType last() const
|
||||||
|
{
|
||||||
|
return m_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IndexType m_first;
|
||||||
|
IndexType m_last;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline IndexInterval<T> makeInterval(T first, T last)
|
||||||
|
{
|
||||||
|
return {first, last};
|
||||||
|
}
|
||||||
|
|
||||||
|
// range is defined via first index and size
|
||||||
|
template <typename Index, typename IndexDiff = Index>
|
||||||
|
class IndexRange
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using IndexType = Index;
|
||||||
|
using IndexDiffType = IndexDiff;
|
||||||
|
|
||||||
|
constexpr IndexRange()
|
||||||
|
: m_first {0}
|
||||||
|
, m_size {0}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexRange(IndexType first, IndexDiffType size)
|
||||||
|
: m_first {first}
|
||||||
|
, m_size {size}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexRange(const IndexInterval<IndexType> &interval)
|
||||||
|
: m_first {interval.first()}
|
||||||
|
, m_size {interval.last() - interval.first() + 1}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexType begin() const
|
||||||
|
{
|
||||||
|
return m_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexType end() const
|
||||||
|
{
|
||||||
|
return m_first + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexDiffType size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexType first() const
|
||||||
|
{
|
||||||
|
return m_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr IndexType last() const
|
||||||
|
{
|
||||||
|
return m_first + m_size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool isEmpty() const
|
||||||
|
{
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IndexType m_first;
|
||||||
|
IndexDiffType m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QBT_INDEXRANGE_H
|
|
@ -16,6 +16,7 @@ peerlistwidget.h
|
||||||
proplistdelegate.h
|
proplistdelegate.h
|
||||||
trackerlist.h
|
trackerlist.h
|
||||||
downloadedpiecesbar.h
|
downloadedpiecesbar.h
|
||||||
|
piecesbar.h
|
||||||
peerlistdelegate.h
|
peerlistdelegate.h
|
||||||
peerlistsortmodel.h
|
peerlistsortmodel.h
|
||||||
peersadditiondlg.h
|
peersadditiondlg.h
|
||||||
|
@ -33,6 +34,7 @@ peerlistwidget.cpp
|
||||||
trackerlist.cpp
|
trackerlist.cpp
|
||||||
peersadditiondlg.cpp
|
peersadditiondlg.cpp
|
||||||
downloadedpiecesbar.cpp
|
downloadedpiecesbar.cpp
|
||||||
|
piecesbar.cpp
|
||||||
trackersadditiondlg.cpp
|
trackersadditiondlg.cpp
|
||||||
pieceavailabilitybar.cpp
|
pieceavailabilitybar.cpp
|
||||||
proptabbar.cpp
|
proptabbar.cpp
|
||||||
|
|
|
@ -28,20 +28,16 @@
|
||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "downloadedpiecesbar.h"
|
#include "downloadedpiecesbar.h"
|
||||||
|
|
||||||
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent)
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent)
|
||||||
|
: base {parent}
|
||||||
|
, m_dlPieceColor {0, 0xd0, 0}
|
||||||
{
|
{
|
||||||
setToolTip(QString("%1\n%2\n%3").arg(tr("White: Missing pieces")).arg(tr("Green: Partial pieces")).arg(tr("Blue: Completed pieces")));
|
|
||||||
|
|
||||||
m_bgColor = 0xffffff;
|
|
||||||
m_borderColor = palette().color(QPalette::Dark).rgb();
|
|
||||||
m_pieceColor = 0x0000ff;
|
|
||||||
m_dlPieceColor = 0x00d000;
|
|
||||||
|
|
||||||
updatePieceColors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize)
|
QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize)
|
||||||
|
@ -49,7 +45,7 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||||
QVector<float> result(reqSize, 0.0);
|
QVector<float> result(reqSize, 0.0);
|
||||||
if (vecin.isEmpty()) return result;
|
if (vecin.isEmpty()) return result;
|
||||||
|
|
||||||
const float ratio = vecin.size() / (float)reqSize;
|
const float ratio = vecin.size() / static_cast<float>(reqSize);
|
||||||
|
|
||||||
// simple linear transformation algorithm
|
// simple linear transformation algorithm
|
||||||
// for example:
|
// for example:
|
||||||
|
@ -62,7 +58,7 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||||
const float toR = (x + 1) * ratio;
|
const float toR = (x + 1) * ratio;
|
||||||
|
|
||||||
// C - integer
|
// C - integer
|
||||||
int fromC = fromR;// std::floor not needed
|
int fromC = fromR; // std::floor not needed
|
||||||
int toC = std::ceil(toR);
|
int toC = std::ceil(toR);
|
||||||
if (toC > vecin.size())
|
if (toC > vecin.size())
|
||||||
--toC;
|
--toC;
|
||||||
|
@ -78,33 +74,28 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||||
|
|
||||||
// case when calculated range is (15.2 >= x < 15.7)
|
// case when calculated range is (15.2 >= x < 15.7)
|
||||||
if (x2 == toCMinusOne) {
|
if (x2 == toCMinusOne) {
|
||||||
if (vecin[x2]) {
|
if (vecin[x2])
|
||||||
value += ratio;
|
value += ratio;
|
||||||
}
|
|
||||||
++x2;
|
++x2;
|
||||||
}
|
}
|
||||||
// case when (15.2 >= x < 17.8)
|
// case when (15.2 >= x < 17.8)
|
||||||
else {
|
else {
|
||||||
// subcase (15.2 >= x < 16)
|
// subcase (15.2 >= x < 16)
|
||||||
if (x2 != fromR) {
|
if (x2 != fromR) {
|
||||||
if (vecin[x2]) {
|
if (vecin[x2])
|
||||||
value += 1.0 - (fromR - fromC);
|
value += 1.0 - (fromR - fromC);
|
||||||
}
|
|
||||||
++x2;
|
++x2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// subcase (16 >= x < 17)
|
// subcase (16 >= x < 17)
|
||||||
for (; x2 < toCMinusOne; ++x2) {
|
for (; x2 < toCMinusOne; ++x2)
|
||||||
if (vecin[x2]) {
|
if (vecin[x2])
|
||||||
value += 1.0;
|
value += 1.0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// subcase (17 >= x < 17.8)
|
// subcase (17 >= x < 17.8)
|
||||||
if (x2 == toCMinusOne) {
|
if (x2 == toCMinusOne) {
|
||||||
if (vecin[x2]) {
|
if (vecin[x2])
|
||||||
value += 1.0 - (toC - toR);
|
value += 1.0 - (toC - toR);
|
||||||
}
|
|
||||||
++x2;
|
++x2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +104,7 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||||
value /= ratio;
|
value /= ratio;
|
||||||
|
|
||||||
// float precision sometimes gives > 1, because in not possible to store irrational numbers
|
// float precision sometimes gives > 1, because in not possible to store irrational numbers
|
||||||
value = qMin(value, (float)1.0);
|
value = qMin(value, 1.0f);
|
||||||
|
|
||||||
result[x] = value;
|
result[x] = value;
|
||||||
}
|
}
|
||||||
|
@ -121,65 +112,43 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DownloadedPiecesBar::updateImage(QImage &image)
|
||||||
int DownloadedPiecesBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
|
|
||||||
{
|
|
||||||
int r1 = qRed(rgb1);
|
|
||||||
int g1 = qGreen(rgb1);
|
|
||||||
int b1 = qBlue(rgb1);
|
|
||||||
|
|
||||||
int r2 = qRed(rgb2);
|
|
||||||
int g2 = qGreen(rgb2);
|
|
||||||
int b2 = qBlue(rgb2);
|
|
||||||
|
|
||||||
float ratio_n = 1.0 - ratio;
|
|
||||||
int r = (r1 * ratio_n) + (r2 * ratio);
|
|
||||||
int g = (g1 * ratio_n) + (g2 * ratio);
|
|
||||||
int b = (b1 * ratio_n) + (b2 * ratio);
|
|
||||||
|
|
||||||
return qRgb(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadedPiecesBar::updateImage()
|
|
||||||
{
|
{
|
||||||
// qDebug() << "updateImage";
|
// qDebug() << "updateImage";
|
||||||
QImage image2(width() - 2, 1, QImage::Format_RGB888);
|
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
|
||||||
if (image2.isNull()) {
|
if (image2.isNull()) {
|
||||||
qDebug() << "QImage image2() allocation failed, width():" << width();
|
qDebug() << "QImage image2() allocation failed, width():" << width();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pieces.isEmpty()) {
|
if (m_pieces.isEmpty()) {
|
||||||
image2.fill(0xffffff);
|
image2.fill(Qt::white);
|
||||||
m_image = image2;
|
image = image2;
|
||||||
update();
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
|
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
|
||||||
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
|
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
|
||||||
|
|
||||||
// filling image
|
// filling image
|
||||||
for (int x = 0; x < scaled_pieces.size(); ++x)
|
for (int x = 0; x < scaled_pieces.size(); ++x) {
|
||||||
{
|
|
||||||
float pieces2_val = scaled_pieces.at(x);
|
float pieces2_val = scaled_pieces.at(x);
|
||||||
float pieces2_val_dl = scaled_pieces_dl.at(x);
|
float pieces2_val_dl = scaled_pieces_dl.at(x);
|
||||||
if (pieces2_val_dl != 0)
|
if (pieces2_val_dl != 0) {
|
||||||
{
|
|
||||||
float fill_ratio = pieces2_val + pieces2_val_dl;
|
float fill_ratio = pieces2_val + pieces2_val_dl;
|
||||||
float ratio = pieces2_val_dl / fill_ratio;
|
float ratio = pieces2_val_dl / fill_ratio;
|
||||||
|
|
||||||
int mixedColor = mixTwoColors(m_pieceColor, m_dlPieceColor, ratio);
|
QRgb mixedColor = mixTwoColors(pieceColor().rgb(), m_dlPieceColor.rgb(), ratio);
|
||||||
mixedColor = mixTwoColors(m_bgColor, mixedColor, fill_ratio);
|
mixedColor = mixTwoColors(backgroundColor().rgb(), mixedColor, fill_ratio);
|
||||||
|
|
||||||
image2.setPixel(x, 0, mixedColor);
|
image2.setPixel(x, 0, mixedColor);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
|
||||||
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_image = image2;
|
image = image2;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces)
|
void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces)
|
||||||
|
@ -187,57 +156,25 @@ void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &
|
||||||
m_pieces = pieces;
|
m_pieces = pieces;
|
||||||
m_downloadedPieces = downloadedPieces;
|
m_downloadedPieces = downloadedPieces;
|
||||||
|
|
||||||
updateImage();
|
requestImageUpdate();
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadedPiecesBar::updatePieceColors()
|
void DownloadedPiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete, const QColor &incomplete)
|
||||||
{
|
{
|
||||||
m_pieceColors = QVector<int>(256);
|
m_dlPieceColor = incomplete;
|
||||||
for (int i = 0; i < 256; ++i) {
|
base::setColors(background, border, complete);
|
||||||
float ratio = (i / 255.0);
|
|
||||||
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadedPiecesBar::clear()
|
void DownloadedPiecesBar::clear()
|
||||||
{
|
{
|
||||||
m_image = QImage();
|
m_pieces.clear();
|
||||||
update();
|
m_downloadedPieces.clear();
|
||||||
|
base::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadedPiecesBar::paintEvent(QPaintEvent *)
|
QString DownloadedPiecesBar::simpleToolTipText() const
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
return tr("White: Missing pieces") + '\n'
|
||||||
QRect imageRect(1, 1, width() - 2, height() - 2);
|
+ tr("Green: Partial pieces") + '\n'
|
||||||
if (m_image.isNull())
|
+ tr("Blue: Completed pieces") + '\n';
|
||||||
{
|
|
||||||
painter.setBrush(Qt::white);
|
|
||||||
painter.drawRect(imageRect);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_image.width() != imageRect.width())
|
|
||||||
updateImage();
|
|
||||||
painter.drawImage(imageRect, m_image);
|
|
||||||
}
|
|
||||||
QPainterPath border;
|
|
||||||
border.addRect(0, 0, width() - 1, height() - 1);
|
|
||||||
|
|
||||||
painter.setPen(m_borderColor);
|
|
||||||
painter.drawPath(border);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadedPiecesBar::setColors(int background, int border, int complete, int incomplete)
|
|
||||||
{
|
|
||||||
m_bgColor = background;
|
|
||||||
m_borderColor = border;
|
|
||||||
m_pieceColor = complete;
|
|
||||||
m_dlPieceColor = incomplete;
|
|
||||||
|
|
||||||
updatePieceColors();
|
|
||||||
updateImage();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,54 +32,39 @@
|
||||||
#define DOWNLOADEDPIECESBAR_H
|
#define DOWNLOADEDPIECESBAR_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QPainter>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QBitArray>
|
#include <QBitArray>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
class DownloadedPiecesBar: public QWidget {
|
#include "piecesbar.h"
|
||||||
|
|
||||||
|
class DownloadedPiecesBar: public PiecesBar
|
||||||
|
{
|
||||||
|
using base = PiecesBar;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(DownloadedPiecesBar)
|
Q_DISABLE_COPY(DownloadedPiecesBar)
|
||||||
|
|
||||||
private:
|
|
||||||
QImage m_image;
|
|
||||||
|
|
||||||
// I used values, because it should be possible to change colors in runtime
|
|
||||||
|
|
||||||
// background color
|
|
||||||
int m_bgColor;
|
|
||||||
// border color
|
|
||||||
int m_borderColor;
|
|
||||||
// complete piece color
|
|
||||||
int m_pieceColor;
|
|
||||||
// incomplete piece color
|
|
||||||
int m_dlPieceColor;
|
|
||||||
// buffered 256 levels gradient from bg_color to piece_color
|
|
||||||
QVector<int> m_pieceColors;
|
|
||||||
|
|
||||||
// last used bitfields, uses to better resize redraw
|
|
||||||
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
|
|
||||||
QBitArray m_pieces;
|
|
||||||
QBitArray m_downloadedPieces;
|
|
||||||
|
|
||||||
// scale bitfield vector to float vector
|
|
||||||
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
|
|
||||||
// mix two colors by light model, ratio <0, 1>
|
|
||||||
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
|
|
||||||
// draw new image and replace actual image
|
|
||||||
void updateImage();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DownloadedPiecesBar(QWidget *parent);
|
DownloadedPiecesBar(QWidget *parent);
|
||||||
|
|
||||||
void setProgress(const QBitArray &m_pieces, const QBitArray &downloadedPieces);
|
void setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces);
|
||||||
void updatePieceColors();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void setColors(int background, int border, int complete, int incomplete);
|
void setColors(const QColor &background, const QColor &border, const QColor &complete, const QColor &incomplete);
|
||||||
|
|
||||||
protected:
|
// PiecesBar interface
|
||||||
void paintEvent(QPaintEvent *);
|
void clear() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// scale bitfield vector to float vector
|
||||||
|
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
|
||||||
|
virtual bool updateImage(QImage &image) override;
|
||||||
|
QString simpleToolTipText() const override;
|
||||||
|
|
||||||
|
// incomplete piece color
|
||||||
|
QColor m_dlPieceColor;
|
||||||
|
// last used bitfields, uses to better resize redraw
|
||||||
|
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
|
||||||
|
QBitArray m_pieces;
|
||||||
|
QBitArray m_downloadedPieces;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DOWNLOADEDPIECESBAR_H
|
#endif // DOWNLOADEDPIECESBAR_H
|
||||||
|
|
|
@ -28,21 +28,15 @@
|
||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "pieceavailabilitybar.h"
|
#include "pieceavailabilitybar.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent)
|
PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent)
|
||||||
: QWidget(parent)
|
: base {parent}
|
||||||
{
|
{
|
||||||
setToolTip(QString("%1\n%2").arg(tr("White: Unavailable pieces")).arg(tr("Blue: Available pieces")));
|
|
||||||
|
|
||||||
m_bgColor = 0xffffff;
|
|
||||||
m_borderColor = palette().color(QPalette::Dark).rgb();
|
|
||||||
m_pieceColor = 0x0000ff;
|
|
||||||
|
|
||||||
updatePieceColors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin, int reqSize)
|
QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin, int reqSize)
|
||||||
|
@ -126,37 +120,18 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PieceAvailabilityBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
|
bool PieceAvailabilityBar::updateImage(QImage &image)
|
||||||
{
|
{
|
||||||
int r1 = qRed(rgb1);
|
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
|
||||||
int g1 = qGreen(rgb1);
|
|
||||||
int b1 = qBlue(rgb1);
|
|
||||||
|
|
||||||
int r2 = qRed(rgb2);
|
|
||||||
int g2 = qGreen(rgb2);
|
|
||||||
int b2 = qBlue(rgb2);
|
|
||||||
|
|
||||||
float ratio_n = 1.0 - ratio;
|
|
||||||
int r = (r1 * ratio_n) + (r2 * ratio);
|
|
||||||
int g = (g1 * ratio_n) + (g2 * ratio);
|
|
||||||
int b = (b1 * ratio_n) + (b2 * ratio);
|
|
||||||
|
|
||||||
return qRgb(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PieceAvailabilityBar::updateImage()
|
|
||||||
{
|
|
||||||
QImage image2(width() - 2, 1, QImage::Format_RGB888);
|
|
||||||
if (image2.isNull()) {
|
if (image2.isNull()) {
|
||||||
qDebug() << "QImage image2() allocation failed, width():" << width();
|
qDebug() << "QImage image2() allocation failed, width():" << width();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pieces.empty()) {
|
if (m_pieces.empty()) {
|
||||||
image2.fill(0xffffff);
|
image2.fill(Qt::white);
|
||||||
m_image = image2;
|
image = image2;
|
||||||
update();
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<float> scaled_pieces = intToFloatVector(m_pieces, image2.width());
|
QVector<float> scaled_pieces = intToFloatVector(m_pieces, image2.width());
|
||||||
|
@ -164,61 +139,32 @@ void PieceAvailabilityBar::updateImage()
|
||||||
// filling image
|
// filling image
|
||||||
for (int x = 0; x < scaled_pieces.size(); ++x) {
|
for (int x = 0; x < scaled_pieces.size(); ++x) {
|
||||||
float pieces2_val = scaled_pieces.at(x);
|
float pieces2_val = scaled_pieces.at(x);
|
||||||
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
|
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
|
||||||
}
|
}
|
||||||
m_image = image2;
|
image = image2;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PieceAvailabilityBar::setAvailability(const QVector<int> &avail)
|
void PieceAvailabilityBar::setAvailability(const QVector<int> &avail)
|
||||||
{
|
{
|
||||||
m_pieces = avail;
|
m_pieces = avail;
|
||||||
|
|
||||||
updateImage();
|
requestImageUpdate();
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PieceAvailabilityBar::updatePieceColors()
|
|
||||||
{
|
|
||||||
m_pieceColors = QVector<int>(256);
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
float ratio = (i / 255.0);
|
|
||||||
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PieceAvailabilityBar::clear()
|
void PieceAvailabilityBar::clear()
|
||||||
{
|
{
|
||||||
m_image = QImage();
|
m_pieces.clear();
|
||||||
update();
|
base::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PieceAvailabilityBar::paintEvent(QPaintEvent *)
|
QString PieceAvailabilityBar::simpleToolTipText() const
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
return tr("White: Unavailable pieces") + '\n'
|
||||||
QRect imageRect(1, 1, width() - 2, height() - 2);
|
+ tr("Blue: Available pieces") + '\n';
|
||||||
if (m_image.isNull()) {
|
|
||||||
painter.setBrush(Qt::white);
|
|
||||||
painter.drawRect(imageRect);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (m_image.width() != imageRect.width())
|
|
||||||
updateImage();
|
|
||||||
painter.drawImage(imageRect, m_image);
|
|
||||||
}
|
|
||||||
QPainterPath border;
|
|
||||||
border.addRect(0, 0, width() - 1, height() - 1);
|
|
||||||
|
|
||||||
painter.setPen(m_borderColor);
|
|
||||||
painter.drawPath(border);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PieceAvailabilityBar::setColors(int background, int border, int available)
|
bool PieceAvailabilityBar::isFileNameCorrectionNeeded() const
|
||||||
{
|
{
|
||||||
m_bgColor = background;
|
return true;
|
||||||
m_borderColor = border;
|
|
||||||
m_pieceColor = available;
|
|
||||||
|
|
||||||
updatePieceColors();
|
|
||||||
updateImage();
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,28 +31,26 @@
|
||||||
#ifndef PIECEAVAILABILITYBAR_H
|
#ifndef PIECEAVAILABILITYBAR_H
|
||||||
#define PIECEAVAILABILITYBAR_H
|
#define PIECEAVAILABILITYBAR_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include "piecesbar.h"
|
||||||
#include <QPainter>
|
|
||||||
#include <QImage>
|
|
||||||
|
|
||||||
class PieceAvailabilityBar: public QWidget
|
class PieceAvailabilityBar: public PiecesBar
|
||||||
{
|
{
|
||||||
|
using base = PiecesBar;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(PieceAvailabilityBar)
|
Q_DISABLE_COPY(PieceAvailabilityBar)
|
||||||
|
|
||||||
|
public:
|
||||||
|
PieceAvailabilityBar(QWidget *parent);
|
||||||
|
|
||||||
|
void setAvailability(const QVector<int> &avail);
|
||||||
|
|
||||||
|
// PiecesBar interface
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QImage m_image;
|
bool updateImage(QImage &image) override;
|
||||||
|
QString simpleToolTipText() const override;
|
||||||
// I used values, because it should be possible to change colors in runtime
|
bool isFileNameCorrectionNeeded() const override;
|
||||||
|
|
||||||
// background color
|
|
||||||
int m_bgColor;
|
|
||||||
// border color
|
|
||||||
int m_borderColor;
|
|
||||||
// complete piece color
|
|
||||||
int m_pieceColor;
|
|
||||||
// buffered 256 levels gradient from bg_color to piece_color
|
|
||||||
QVector<int> m_pieceColors;
|
|
||||||
|
|
||||||
// last used int vector, uses to better resize redraw
|
// last used int vector, uses to better resize redraw
|
||||||
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
|
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
|
||||||
|
@ -60,23 +58,6 @@ private:
|
||||||
|
|
||||||
// scale int vector to float vector
|
// scale int vector to float vector
|
||||||
QVector<float> intToFloatVector(const QVector<int> &vecin, int reqSize);
|
QVector<float> intToFloatVector(const QVector<int> &vecin, int reqSize);
|
||||||
|
|
||||||
// mix two colors by light model, ratio <0, 1>
|
|
||||||
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
|
|
||||||
// draw new image and replace actual image
|
|
||||||
void updateImage();
|
|
||||||
|
|
||||||
public:
|
|
||||||
PieceAvailabilityBar(QWidget *parent);
|
|
||||||
|
|
||||||
void setAvailability(const QVector<int> &avail);
|
|
||||||
void updatePieceColors();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void setColors(int background, int border, int available);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PIECEAVAILABILITYBAR_H
|
#endif // PIECEAVAILABILITYBAR_H
|
||||||
|
|
334
src/gui/properties/piecesbar.cpp
Normal file
334
src/gui/properties/piecesbar.cpp
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2016 Eugene Shalygin
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "piecesbar.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QHelpEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
|
#include "base/bittorrent/torrenthandle.h"
|
||||||
|
#include "base/utils/misc.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using ImageRange = IndexRange<int>;
|
||||||
|
|
||||||
|
// Computes approximate mapping from image scale (measured in pixels) onto the torrent contents scale (in pieces)
|
||||||
|
// However, taking the size of a screen to be ~ 1000 px and the torrent size larger than 10 MiB, the pointing error
|
||||||
|
// is well below 0.5 px and thus is negligible.
|
||||||
|
class PieceIndexToImagePos
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
|
||||||
|
: m_bytesPerPixel {image.width() > 0 ? torrentInfo.totalSize() / image.width() : -1}
|
||||||
|
, m_torrentInfo {torrentInfo}
|
||||||
|
{
|
||||||
|
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
|
||||||
|
qDebug() << "PieceIndexToImagePos: torrent size is too small for correct computaions."
|
||||||
|
<< "Torrent size =" << torrentInfo.totalSize() << "Image width = " << image.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageRange imagePos(const BitTorrent::TorrentInfo::PieceRange &pieces) const
|
||||||
|
{
|
||||||
|
if (m_bytesPerPixel < 0)
|
||||||
|
return {0, 0};
|
||||||
|
|
||||||
|
// the type conversion is used to prevent integer overflow with torrents of 2+ GiB size
|
||||||
|
const qlonglong pieceLength = m_torrentInfo.pieceLength();
|
||||||
|
return makeInterval<ImageRange::IndexType>(
|
||||||
|
(pieces.first() * pieceLength) / m_bytesPerPixel,
|
||||||
|
(pieces.last() * pieceLength + m_torrentInfo.pieceLength(pieces.last()) - 1) / m_bytesPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pieceIndex(int imagePos) const
|
||||||
|
{
|
||||||
|
return m_bytesPerPixel < 0 ? 0 : (imagePos * m_bytesPerPixel + m_bytesPerPixel / 2) / m_torrentInfo.pieceLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const qlonglong m_bytesPerPixel; // how many bytes of the torrent are squeezed into a bar's pixel
|
||||||
|
const BitTorrent::TorrentInfo m_torrentInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DetailedTooltipRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DetailedTooltipRenderer(QTextStream &stream, const QString &header)
|
||||||
|
: m_stream(stream)
|
||||||
|
{
|
||||||
|
m_stream << header
|
||||||
|
<< R"(<table style="width:100%; padding: 3px; vertical-align: middle;">)";
|
||||||
|
}
|
||||||
|
|
||||||
|
~DetailedTooltipRenderer()
|
||||||
|
{
|
||||||
|
m_stream << "</table>";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const QString &size, const QString &path)
|
||||||
|
{
|
||||||
|
m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path << "</td></tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTextStream &m_stream;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PiecesBar::PiecesBar(QWidget *parent)
|
||||||
|
: QWidget {parent}
|
||||||
|
, m_torrent {nullptr}
|
||||||
|
, m_borderColor {palette().color(QPalette::Dark)}
|
||||||
|
, m_bgColor {Qt::white}
|
||||||
|
, m_pieceColor {Qt::blue}
|
||||||
|
, m_hovered {false}
|
||||||
|
{
|
||||||
|
updatePieceColors();
|
||||||
|
setMouseTracking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::setTorrent(BitTorrent::TorrentHandle *torrent)
|
||||||
|
{
|
||||||
|
m_torrent = torrent;
|
||||||
|
if (!m_torrent)
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::clear()
|
||||||
|
{
|
||||||
|
m_image = QImage();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete)
|
||||||
|
{
|
||||||
|
m_bgColor = background;
|
||||||
|
m_borderColor = border;
|
||||||
|
m_pieceColor = complete;
|
||||||
|
|
||||||
|
updatePieceColors();
|
||||||
|
requestImageUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PiecesBar::event(QEvent *e)
|
||||||
|
{
|
||||||
|
if (e->type() == QEvent::ToolTip) {
|
||||||
|
showToolTip(static_cast<QHelpEvent *>(e));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return base::event(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::enterEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
m_hovered = true;
|
||||||
|
base::enterEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::leaveEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
m_hovered = false;
|
||||||
|
m_highlitedRegion = QRect();
|
||||||
|
requestImageUpdate();
|
||||||
|
base::leaveEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::mouseMoveEvent(QMouseEvent *e)
|
||||||
|
{
|
||||||
|
// if user pointed to a piece which is a part of a single large file,
|
||||||
|
// we highlight the space, occupied by this file
|
||||||
|
highlightFile(e->pos().x() - borderWidth);
|
||||||
|
base::mouseMoveEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
QRect imageRect(borderWidth, borderWidth, width() - 2 * borderWidth, height() - 2 * borderWidth);
|
||||||
|
if (m_image.isNull()) {
|
||||||
|
painter.setBrush(Qt::white);
|
||||||
|
painter.drawRect(imageRect);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (m_image.width() != imageRect.width())
|
||||||
|
updateImage(m_image);
|
||||||
|
painter.drawImage(imageRect, m_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_highlitedRegion.isNull()) {
|
||||||
|
QColor highlightColor {this->palette().color(QPalette::Active, QPalette::Highlight)};
|
||||||
|
highlightColor.setAlphaF(0.35);
|
||||||
|
QRect targetHighlightRect {m_highlitedRegion.adjusted(borderWidth, borderWidth, borderWidth, height() - 2 * borderWidth)};
|
||||||
|
painter.fillRect(targetHighlightRect, highlightColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath border;
|
||||||
|
border.addRect(0, 0, width(), height());
|
||||||
|
painter.setPen(m_borderColor);
|
||||||
|
painter.drawPath(border);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::requestImageUpdate()
|
||||||
|
{
|
||||||
|
if (updateImage(m_image))
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PiecesBar::backgroundColor() const
|
||||||
|
{
|
||||||
|
return m_bgColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PiecesBar::borderColor() const
|
||||||
|
{
|
||||||
|
return m_borderColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PiecesBar::pieceColor() const
|
||||||
|
{
|
||||||
|
return m_pieceColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<QRgb> &PiecesBar::pieceColors() const
|
||||||
|
{
|
||||||
|
return m_pieceColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRgb PiecesBar::mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio)
|
||||||
|
{
|
||||||
|
int r1 = qRed(rgb1);
|
||||||
|
int g1 = qGreen(rgb1);
|
||||||
|
int b1 = qBlue(rgb1);
|
||||||
|
|
||||||
|
int r2 = qRed(rgb2);
|
||||||
|
int g2 = qGreen(rgb2);
|
||||||
|
int b2 = qBlue(rgb2);
|
||||||
|
|
||||||
|
float ratioN = 1.0f - ratio;
|
||||||
|
int r = (r1 * ratioN) + (r2 * ratio);
|
||||||
|
int g = (g1 * ratioN) + (g2 * ratio);
|
||||||
|
int b = (b1 * ratioN) + (b2 * ratio);
|
||||||
|
|
||||||
|
return qRgb(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::showToolTip(const QHelpEvent *e)
|
||||||
|
{
|
||||||
|
if (!m_torrent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString toolTipText;
|
||||||
|
QTextStream stream(&toolTipText, QIODevice::WriteOnly);
|
||||||
|
bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
||||||
|
if (showDetailedInformation) {
|
||||||
|
stream << "<html><body>";
|
||||||
|
const int imagePos = e->pos().x() - borderWidth;
|
||||||
|
if ((imagePos >=0) && (imagePos < m_image.width())) {
|
||||||
|
PieceIndexToImagePos transform {m_torrent->info(), m_image};
|
||||||
|
int pieceIndex = transform.pieceIndex(e->pos().x() - borderWidth);
|
||||||
|
QVector<int> files {m_torrent->info().fileIndicesForPiece(pieceIndex)};
|
||||||
|
|
||||||
|
QString tooltipTitle;
|
||||||
|
if (files.count() > 1) {
|
||||||
|
tooltipTitle = tr("Files in this piece:");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (m_torrent->info().fileSize(files.front()) == m_torrent->info().pieceLength(pieceIndex))
|
||||||
|
tooltipTitle = tr("File in this piece");
|
||||||
|
else
|
||||||
|
tooltipTitle = tr("File in these pieces");
|
||||||
|
}
|
||||||
|
|
||||||
|
DetailedTooltipRenderer renderer(stream, tooltipTitle);
|
||||||
|
|
||||||
|
const bool isFileNameCorrectionNeeded = this->isFileNameCorrectionNeeded();
|
||||||
|
for (int f: files) {
|
||||||
|
QString filePath {m_torrent->info().filePath(f)};
|
||||||
|
if (isFileNameCorrectionNeeded)
|
||||||
|
filePath.replace(QLatin1String("/.unwanted"), QString());
|
||||||
|
|
||||||
|
renderer(Utils::Misc::friendlyUnit(m_torrent->info().fileSize(f)), filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream << "</body></html>";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
stream << simpleToolTipText();
|
||||||
|
stream << '\n' << tr("Hold Shift key for detailed information");
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.flush();
|
||||||
|
|
||||||
|
QToolTip::showText(e->globalPos(), toolTipText, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::highlightFile(int imagePos)
|
||||||
|
{
|
||||||
|
if (!m_torrent || (imagePos < 0) || (imagePos >= m_image.width()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PieceIndexToImagePos transform {m_torrent->info(), m_image};
|
||||||
|
|
||||||
|
int pieceIndex = transform.pieceIndex(imagePos);
|
||||||
|
QVector<int> fileIndices {m_torrent->info().fileIndicesForPiece(pieceIndex)};
|
||||||
|
if (fileIndices.count() == 1) {
|
||||||
|
BitTorrent::TorrentInfo::PieceRange filePieces = m_torrent->info().filePieces(fileIndices.first());
|
||||||
|
|
||||||
|
ImageRange imageRange = transform.imagePos(filePieces);
|
||||||
|
QRect newHighlitedRegion {imageRange.first(), 0, imageRange.size(), m_image.height()};
|
||||||
|
if (newHighlitedRegion != m_highlitedRegion) {
|
||||||
|
m_highlitedRegion = newHighlitedRegion;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!m_highlitedRegion.isEmpty()) {
|
||||||
|
m_highlitedRegion = QRect();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PiecesBar::updatePieceColors()
|
||||||
|
{
|
||||||
|
m_pieceColors = QVector<QRgb>(256);
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
float ratio = (i / 255.0);
|
||||||
|
m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), m_pieceColor.rgb(), ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PiecesBar::isFileNameCorrectionNeeded() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
109
src/gui/properties/piecesbar.h
Normal file
109
src/gui/properties/piecesbar.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2016 Eugene Shalygin
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PIECESBAR_H
|
||||||
|
#define PIECESBAR_H
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QHelpEvent;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class TorrentHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PiecesBar: public QWidget
|
||||||
|
{
|
||||||
|
using base = QWidget;
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(PiecesBar)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PiecesBar(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setTorrent(BitTorrent::TorrentHandle *torrent);
|
||||||
|
void setColors(const QColor &background, const QColor &border, const QColor &complete);
|
||||||
|
|
||||||
|
virtual void clear();
|
||||||
|
|
||||||
|
// QObject interface
|
||||||
|
virtual bool event(QEvent*) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// QWidget interface
|
||||||
|
void enterEvent(QEvent*) override;
|
||||||
|
void leaveEvent(QEvent*) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent*) override;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
void requestImageUpdate();
|
||||||
|
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
QColor borderColor() const;
|
||||||
|
QColor pieceColor() const;
|
||||||
|
const QVector<QRgb> &pieceColors() const;
|
||||||
|
|
||||||
|
// mix two colors by light model, ratio <0, 1>
|
||||||
|
static QRgb mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio);
|
||||||
|
|
||||||
|
static constexpr int borderWidth = 1;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void showToolTip(const QHelpEvent*);
|
||||||
|
void highlightFile(int imagePos);
|
||||||
|
|
||||||
|
virtual QString simpleToolTipText() const = 0;
|
||||||
|
|
||||||
|
/// whether to perform removing of ".unwanted" directory from paths
|
||||||
|
virtual bool isFileNameCorrectionNeeded() const;
|
||||||
|
|
||||||
|
// draw new image to replace the actual image
|
||||||
|
// returns true if image was successfully updated
|
||||||
|
virtual bool updateImage(QImage &image) = 0;
|
||||||
|
void updatePieceColors();
|
||||||
|
|
||||||
|
const BitTorrent::TorrentHandle *m_torrent;
|
||||||
|
QImage m_image;
|
||||||
|
// I used values, because it should be possible to change colors at run time
|
||||||
|
// border color
|
||||||
|
QColor m_borderColor;
|
||||||
|
// background color
|
||||||
|
QColor m_bgColor;
|
||||||
|
// complete piece color
|
||||||
|
QColor m_pieceColor;
|
||||||
|
// buffered 256 levels gradient from bg_color to piece_color
|
||||||
|
QVector<QRgb> m_pieceColors;
|
||||||
|
bool m_hovered;
|
||||||
|
QRect m_highlitedRegion; //!< part of the bar can be highlighted; this rectangle is in the same frame as m_image
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PIECESBAR_H
|
|
@ -16,7 +16,8 @@ HEADERS += $$PWD/propertieswidget.h \
|
||||||
$$PWD/pieceavailabilitybar.h \
|
$$PWD/pieceavailabilitybar.h \
|
||||||
$$PWD/proptabbar.h \
|
$$PWD/proptabbar.h \
|
||||||
$$PWD/speedwidget.h \
|
$$PWD/speedwidget.h \
|
||||||
$$PWD/speedplotview.h
|
$$PWD/speedplotview.h \
|
||||||
|
$$PWD/piecesbar.h
|
||||||
|
|
||||||
SOURCES += $$PWD/propertieswidget.cpp \
|
SOURCES += $$PWD/propertieswidget.cpp \
|
||||||
$$PWD/proplistdelegate.cpp \
|
$$PWD/proplistdelegate.cpp \
|
||||||
|
@ -28,4 +29,5 @@ SOURCES += $$PWD/propertieswidget.cpp \
|
||||||
$$PWD/pieceavailabilitybar.cpp \
|
$$PWD/pieceavailabilitybar.cpp \
|
||||||
$$PWD/proptabbar.cpp \
|
$$PWD/proptabbar.cpp \
|
||||||
$$PWD/speedwidget.cpp \
|
$$PWD/speedwidget.cpp \
|
||||||
$$PWD/speedplotview.cpp
|
$$PWD/speedplotview.cpp \
|
||||||
|
$$PWD/piecesbar.cpp
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "propertieswidget.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QListWidgetItem>
|
#include <QListWidgetItem>
|
||||||
|
@ -61,10 +63,10 @@
|
||||||
#include "lineedit.h"
|
#include "lineedit.h"
|
||||||
#include "transferlistwidget.h"
|
#include "transferlistwidget.h"
|
||||||
#include "autoexpandabledialog.h"
|
#include "autoexpandabledialog.h"
|
||||||
#include "propertieswidget.h"
|
|
||||||
|
|
||||||
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList):
|
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList)
|
||||||
QWidget(parent), transferList(transferList), main_window(main_window), m_torrent(0) {
|
: QWidget(parent), transferList(transferList), main_window(main_window), m_torrent(0)
|
||||||
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
setAutoFillBackground(true);
|
setAutoFillBackground(true);
|
||||||
|
|
||||||
|
@ -88,17 +90,17 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra
|
||||||
connect(selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll()));
|
connect(selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll()));
|
||||||
connect(selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone()));
|
connect(selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone()));
|
||||||
connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
|
connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
|
||||||
connect(filesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(openDoubleClickedFile(const QModelIndex &)));
|
connect(filesList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(openDoubleClickedFile(const QModelIndex&)));
|
||||||
connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
||||||
connect(listWebSeeds, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayWebSeedListMenu(const QPoint&)));
|
connect(listWebSeeds, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayWebSeedListMenu(const QPoint&)));
|
||||||
connect(transferList, SIGNAL(currentTorrentChanged(BitTorrent::TorrentHandle *const)), this, SLOT(loadTorrentInfos(BitTorrent::TorrentHandle *const)));
|
connect(transferList, SIGNAL(currentTorrentChanged(BitTorrent::TorrentHandle * const)), this, SLOT(loadTorrentInfos(BitTorrent::TorrentHandle * const)));
|
||||||
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
||||||
connect(stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData()));
|
connect(stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData()));
|
||||||
connect(BitTorrent::Session::instance(), SIGNAL(torrentSavePathChanged(BitTorrent::TorrentHandle *const)), this, SLOT(updateSavePath(BitTorrent::TorrentHandle *const)));
|
connect(BitTorrent::Session::instance(), SIGNAL(torrentSavePathChanged(BitTorrent::TorrentHandle * const)), this, SLOT(updateSavePath(BitTorrent::TorrentHandle * const)));
|
||||||
connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle *const)), this, SLOT(updateTorrentInfos(BitTorrent::TorrentHandle *const)));
|
connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle * const)), this, SLOT(updateTorrentInfos(BitTorrent::TorrentHandle * const)));
|
||||||
connect(filesList->header(), SIGNAL(sectionMoved(int, int, int)), this, SLOT(saveSettings()));
|
connect(filesList->header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(saveSettings()));
|
||||||
connect(filesList->header(), SIGNAL(sectionResized(int, int, int)), this, SLOT(saveSettings()));
|
connect(filesList->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(saveSettings()));
|
||||||
connect(filesList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(saveSettings()));
|
connect(filesList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSettings()));
|
||||||
|
|
||||||
#ifdef QBT_USES_QT5
|
#ifdef QBT_USES_QT5
|
||||||
// set bar height relative to screen dpi
|
// set bar height relative to screen dpi
|
||||||
|
@ -133,15 +135,15 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra
|
||||||
connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
|
connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
|
||||||
connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
|
connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
|
||||||
horizontalLayout_trackers->insertWidget(0, trackerList);
|
horizontalLayout_trackers->insertWidget(0, trackerList);
|
||||||
connect(trackerList->header(), SIGNAL(sectionMoved(int, int, int)), trackerList, SLOT(saveSettings()));
|
connect(trackerList->header(), SIGNAL(sectionMoved(int,int,int)), trackerList, SLOT(saveSettings()));
|
||||||
connect(trackerList->header(), SIGNAL(sectionResized(int, int, int)), trackerList, SLOT(saveSettings()));
|
connect(trackerList->header(), SIGNAL(sectionResized(int,int,int)), trackerList, SLOT(saveSettings()));
|
||||||
connect(trackerList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), trackerList, SLOT(saveSettings()));
|
connect(trackerList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), trackerList, SLOT(saveSettings()));
|
||||||
// Peers list
|
// Peers list
|
||||||
peersList = new PeerListWidget(this);
|
peersList = new PeerListWidget(this);
|
||||||
peerpage_layout->addWidget(peersList);
|
peerpage_layout->addWidget(peersList);
|
||||||
connect(peersList->header(), SIGNAL(sectionMoved(int, int, int)), peersList, SLOT(saveSettings()));
|
connect(peersList->header(), SIGNAL(sectionMoved(int,int,int)), peersList, SLOT(saveSettings()));
|
||||||
connect(peersList->header(), SIGNAL(sectionResized(int, int, int)), peersList, SLOT(saveSettings()));
|
connect(peersList->header(), SIGNAL(sectionResized(int,int,int)), peersList, SLOT(saveSettings()));
|
||||||
connect(peersList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), peersList, SLOT(saveSettings()));
|
connect(peersList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), peersList, SLOT(saveSettings()));
|
||||||
// Speed widget
|
// Speed widget
|
||||||
speedWidget = new SpeedWidget(this);
|
speedWidget = new SpeedWidget(this);
|
||||||
speed_layout->addWidget(speedWidget);
|
speed_layout->addWidget(speedWidget);
|
||||||
|
@ -168,7 +170,8 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra
|
||||||
connect(openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile()));
|
connect(openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertiesWidget::~PropertiesWidget() {
|
PropertiesWidget::~PropertiesWidget()
|
||||||
|
{
|
||||||
qDebug() << Q_FUNC_INFO << "ENTER";
|
qDebug() << Q_FUNC_INFO << "ENTER";
|
||||||
delete refreshTimer;
|
delete refreshTimer;
|
||||||
delete trackerList;
|
delete trackerList;
|
||||||
|
@ -186,7 +189,8 @@ PropertiesWidget::~PropertiesWidget() {
|
||||||
qDebug() << Q_FUNC_INFO << "EXIT";
|
qDebug() << Q_FUNC_INFO << "EXIT";
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::showPiecesAvailability(bool show) {
|
void PropertiesWidget::showPiecesAvailability(bool show)
|
||||||
|
{
|
||||||
avail_pieces_lbl->setVisible(show);
|
avail_pieces_lbl->setVisible(show);
|
||||||
pieces_availability->setVisible(show);
|
pieces_availability->setVisible(show);
|
||||||
avail_average_lbl->setVisible(show);
|
avail_average_lbl->setVisible(show);
|
||||||
|
@ -194,7 +198,8 @@ void PropertiesWidget::showPiecesAvailability(bool show) {
|
||||||
line_2->setVisible(show);
|
line_2->setVisible(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::showPiecesDownloaded(bool show) {
|
void PropertiesWidget::showPiecesDownloaded(bool show)
|
||||||
|
{
|
||||||
downloaded_pieces_lbl->setVisible(show);
|
downloaded_pieces_lbl->setVisible(show);
|
||||||
downloaded_pieces->setVisible(show);
|
downloaded_pieces->setVisible(show);
|
||||||
progress_lbl->setVisible(show);
|
progress_lbl->setVisible(show);
|
||||||
|
@ -202,22 +207,23 @@ void PropertiesWidget::showPiecesDownloaded(bool show) {
|
||||||
line_2->setVisible(show);
|
line_2->setVisible(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::setVisibility(bool visible) {
|
void PropertiesWidget::setVisibility(bool visible)
|
||||||
if (!visible && state == VISIBLE) {
|
{
|
||||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
if (!visible && ( state == VISIBLE) ) {
|
||||||
|
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||||
stackedProperties->setVisible(false);
|
stackedProperties->setVisible(false);
|
||||||
slideSizes = hSplitter->sizes();
|
slideSizes = hSplitter->sizes();
|
||||||
hSplitter->handle(1)->setVisible(false);
|
hSplitter->handle(1)->setVisible(false);
|
||||||
hSplitter->handle(1)->setDisabled(true);
|
hSplitter->handle(1)->setDisabled(true);
|
||||||
QList<int> sizes = QList<int>() << hSplitter->geometry().height()-30 << 30;
|
QList<int> sizes = QList<int>() << hSplitter->geometry().height() - 30 << 30;
|
||||||
hSplitter->setSizes(sizes);
|
hSplitter->setSizes(sizes);
|
||||||
state = REDUCED;
|
state = REDUCED;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visible && state == REDUCED) {
|
if (visible && ( state == REDUCED) ) {
|
||||||
stackedProperties->setVisible(true);
|
stackedProperties->setVisible(true);
|
||||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||||
hSplitter->handle(1)->setDisabled(false);
|
hSplitter->handle(1)->setDisabled(false);
|
||||||
hSplitter->handle(1)->setVisible(true);
|
hSplitter->handle(1)->setVisible(true);
|
||||||
hSplitter->setSizes(slideSizes);
|
hSplitter->setSizes(slideSizes);
|
||||||
|
@ -227,7 +233,8 @@ void PropertiesWidget::setVisibility(bool visible) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::clear() {
|
void PropertiesWidget::clear()
|
||||||
|
{
|
||||||
qDebug("Clearing torrent properties");
|
qDebug("Clearing torrent properties");
|
||||||
save_path->clear();
|
save_path->clear();
|
||||||
lbl_creationDate->clear();
|
lbl_creationDate->clear();
|
||||||
|
@ -271,9 +278,8 @@ BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const
|
||||||
|
|
||||||
void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent)
|
void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
if (m_torrent == torrent) {
|
if (m_torrent == torrent)
|
||||||
save_path->setText(Utils::Fs::toNativePath(m_torrent->savePath()));
|
save_path->setText(Utils::Fs::toNativePath(m_torrent->savePath()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent)
|
void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent)
|
||||||
|
@ -292,6 +298,8 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_torrent = torrent;
|
m_torrent = torrent;
|
||||||
|
downloaded_pieces->setTorrent(m_torrent);
|
||||||
|
pieces_availability->setTorrent(m_torrent);
|
||||||
if (!m_torrent) return;
|
if (!m_torrent) return;
|
||||||
|
|
||||||
// Save path
|
// Save path
|
||||||
|
@ -324,59 +332,60 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
|
||||||
loadDynamicData();
|
loadDynamicData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::readSettings() {
|
void PropertiesWidget::readSettings()
|
||||||
const Preferences* const pref = Preferences::instance();
|
{
|
||||||
|
const Preferences *const pref = Preferences::instance();
|
||||||
// Restore splitter sizes
|
// Restore splitter sizes
|
||||||
QStringList sizes_str = pref->getPropSplitterSizes().split(",");
|
QStringList sizes_str = pref->getPropSplitterSizes().split(",");
|
||||||
if (sizes_str.size() == 2) {
|
if (sizes_str.size() == 2) {
|
||||||
slideSizes << sizes_str.first().toInt();
|
slideSizes << sizes_str.first().toInt();
|
||||||
slideSizes << sizes_str.last().toInt();
|
slideSizes << sizes_str.last().toInt();
|
||||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||||
hSplitter->setSizes(slideSizes);
|
hSplitter->setSizes(slideSizes);
|
||||||
}
|
}
|
||||||
const int current_tab = pref->getPropCurTab();
|
const int current_tab = pref->getPropCurTab();
|
||||||
const bool visible = pref->getPropVisible();
|
const bool visible = pref->getPropVisible();
|
||||||
// the following will call saveSettings but shouldn't change any state
|
// the following will call saveSettings but shouldn't change any state
|
||||||
if (!filesList->header()->restoreState(pref->getPropFileListState())) {
|
if (!filesList->header()->restoreState(pref->getPropFileListState()))
|
||||||
filesList->header()->resizeSection(0, 400); //Default
|
filesList->header()->resizeSection(0, 400); // Default
|
||||||
}
|
|
||||||
m_tabBar->setCurrentIndex(current_tab);
|
m_tabBar->setCurrentIndex(current_tab);
|
||||||
if (!visible) {
|
if (!visible)
|
||||||
setVisibility(false);
|
setVisibility(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::saveSettings() {
|
void PropertiesWidget::saveSettings()
|
||||||
Preferences* const pref = Preferences::instance();
|
{
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
pref->setPropVisible(state==VISIBLE);
|
pref->setPropVisible(state==VISIBLE);
|
||||||
// Splitter sizes
|
// Splitter sizes
|
||||||
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||||
QList<int> sizes;
|
QList<int> sizes;
|
||||||
if (state == VISIBLE)
|
if (state == VISIBLE)
|
||||||
sizes = hSplitter->sizes();
|
sizes = hSplitter->sizes();
|
||||||
else
|
else
|
||||||
sizes = slideSizes;
|
sizes = slideSizes;
|
||||||
qDebug("Sizes: %d", sizes.size());
|
qDebug("Sizes: %d", sizes.size());
|
||||||
if (sizes.size() == 2) {
|
if (sizes.size() == 2)
|
||||||
pref->setPropSplitterSizes(QString::number(sizes.first()) + ',' + QString::number(sizes.last()));
|
pref->setPropSplitterSizes(QString::number(sizes.first()) + ',' + QString::number(sizes.last()));
|
||||||
}
|
|
||||||
pref->setPropFileListState(filesList->header()->saveState());
|
pref->setPropFileListState(filesList->header()->saveState());
|
||||||
// Remember current tab
|
// Remember current tab
|
||||||
pref->setPropCurTab(m_tabBar->currentIndex());
|
pref->setPropCurTab(m_tabBar->currentIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::reloadPreferences() {
|
void PropertiesWidget::reloadPreferences()
|
||||||
|
{
|
||||||
// Take program preferences into consideration
|
// Take program preferences into consideration
|
||||||
peersList->updatePeerHostNameResolutionState();
|
peersList->updatePeerHostNameResolutionState();
|
||||||
peersList->updatePeerCountryResolutionState();
|
peersList->updatePeerCountryResolutionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::loadDynamicData() {
|
void PropertiesWidget::loadDynamicData()
|
||||||
|
{
|
||||||
// Refresh only if the torrent handle is valid and if visible
|
// Refresh only if the torrent handle is valid and if visible
|
||||||
if (!m_torrent || (main_window->currentTabWidget() != transferList) || (state != VISIBLE)) return;
|
if (!m_torrent || (main_window->currentTabWidget() != transferList) || (state != VISIBLE)) return;
|
||||||
|
|
||||||
// Transfer infos
|
// Transfer infos
|
||||||
switch(stackedProperties->currentIndex()) {
|
switch (stackedProperties->currentIndex()) {
|
||||||
case PropTabBar::MAIN_TAB: {
|
case PropTabBar::MAIN_TAB: {
|
||||||
wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
|
wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
|
||||||
|
|
||||||
|
@ -449,7 +458,7 @@ void PropertiesWidget::loadDynamicData() {
|
||||||
|
|
||||||
// Progress
|
// Progress
|
||||||
qreal progress = m_torrent->progress() * 100.;
|
qreal progress = m_torrent->progress() * 100.;
|
||||||
progress_lbl->setText(Utils::String::fromDouble(progress, 1)+"%");
|
progress_lbl->setText(Utils::String::fromDouble(progress, 1) + "%");
|
||||||
downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
|
downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -490,7 +499,8 @@ void PropertiesWidget::loadDynamicData() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::loadUrlSeeds() {
|
void PropertiesWidget::loadUrlSeeds()
|
||||||
|
{
|
||||||
listWebSeeds->clear();
|
listWebSeeds->clear();
|
||||||
qDebug("Loading URL seeds");
|
qDebug("Loading URL seeds");
|
||||||
const QList<QUrl> hc_seeds = m_torrent->urlSeeds();
|
const QList<QUrl> hc_seeds = m_torrent->urlSeeds();
|
||||||
|
@ -501,7 +511,8 @@ void PropertiesWidget::loadUrlSeeds() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) {
|
void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index)
|
||||||
|
{
|
||||||
if (!index.isValid()) return;
|
if (!index.isValid()) return;
|
||||||
if (!m_torrent || !m_torrent->hasMetadata()) return;
|
if (!m_torrent || !m_torrent->hasMetadata()) return;
|
||||||
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType)
|
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType)
|
||||||
|
@ -510,7 +521,8 @@ void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) {
|
||||||
openFolder(index, false);
|
openFolder(index, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::openFile(const QModelIndex &index) {
|
void PropertiesWidget::openFile(const QModelIndex &index)
|
||||||
|
{
|
||||||
int i = PropListModel->getFileIndex(index);
|
int i = PropListModel->getFileIndex(index);
|
||||||
const QDir saveDir(m_torrent->savePath(true));
|
const QDir saveDir(m_torrent->savePath(true));
|
||||||
const QString filename = m_torrent->filePath(i);
|
const QString filename = m_torrent->filePath(i);
|
||||||
|
@ -521,7 +533,8 @@ void PropertiesWidget::openFile(const QModelIndex &index) {
|
||||||
Utils::Misc::openPath(file_path);
|
Utils::Misc::openPath(file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_folder) {
|
void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_folder)
|
||||||
|
{
|
||||||
QString absolute_path;
|
QString absolute_path;
|
||||||
// FOLDER
|
// FOLDER
|
||||||
if (PropListModel->itemType(index) == TorrentContentModelItem::FolderType) {
|
if (PropListModel->itemType(index) == TorrentContentModelItem::FolderType) {
|
||||||
|
@ -529,7 +542,7 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
|
||||||
QStringList path_items;
|
QStringList path_items;
|
||||||
path_items << index.data().toString();
|
path_items << index.data().toString();
|
||||||
QModelIndex parent = PropListModel->parent(index);
|
QModelIndex parent = PropListModel->parent(index);
|
||||||
while(parent.isValid()) {
|
while (parent.isValid()) {
|
||||||
path_items.prepend(parent.data().toString());
|
path_items.prepend(parent.data().toString());
|
||||||
parent = PropListModel->parent(parent);
|
parent = PropListModel->parent(parent);
|
||||||
}
|
}
|
||||||
|
@ -554,7 +567,8 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
|
||||||
Utils::Misc::openPath(absolute_path);
|
Utils::Misc::openPath(absolute_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::displayFilesListMenu(const QPoint&) {
|
void PropertiesWidget::displayFilesListMenu(const QPoint &)
|
||||||
|
{
|
||||||
if (!m_torrent) return;
|
if (!m_torrent) return;
|
||||||
|
|
||||||
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
|
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
|
||||||
|
@ -587,12 +601,15 @@ void PropertiesWidget::displayFilesListMenu(const QPoint&) {
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return;
|
return;
|
||||||
if (act) {
|
if (act) {
|
||||||
if (act == actOpen)
|
if (act == actOpen) {
|
||||||
openDoubleClickedFile(index);
|
openDoubleClickedFile(index);
|
||||||
else if (act == actOpenContainingFolder)
|
}
|
||||||
|
else if (act == actOpenContainingFolder) {
|
||||||
openFolder(index, true);
|
openFolder(index, true);
|
||||||
else if (act == actRename)
|
}
|
||||||
|
else if (act == actRename) {
|
||||||
renameSelectedFile();
|
renameSelectedFile();
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
int prio = prio::NORMAL;
|
int prio = prio::NORMAL;
|
||||||
if (act == actionHigh)
|
if (act == actionHigh)
|
||||||
|
@ -613,7 +630,8 @@ void PropertiesWidget::displayFilesListMenu(const QPoint&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::displayWebSeedListMenu(const QPoint&) {
|
void PropertiesWidget::displayWebSeedListMenu(const QPoint &)
|
||||||
|
{
|
||||||
if (!m_torrent) return;
|
if (!m_torrent) return;
|
||||||
|
|
||||||
QMenu seedMenu;
|
QMenu seedMenu;
|
||||||
|
@ -643,7 +661,8 @@ void PropertiesWidget::displayWebSeedListMenu(const QPoint&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::renameSelectedFile() {
|
void PropertiesWidget::renameSelectedFile()
|
||||||
|
{
|
||||||
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0);
|
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0);
|
||||||
if (selectedIndexes.size() != 1)
|
if (selectedIndexes.size() != 1)
|
||||||
return;
|
return;
|
||||||
|
@ -667,9 +686,8 @@ void PropertiesWidget::renameSelectedFile() {
|
||||||
const int file_index = PropListModel->getFileIndex(index);
|
const int file_index = PropListModel->getFileIndex(index);
|
||||||
if (!m_torrent || !m_torrent->hasMetadata()) return;
|
if (!m_torrent || !m_torrent->hasMetadata()) return;
|
||||||
QString old_name = m_torrent->filePath(file_index);
|
QString old_name = m_torrent->filePath(file_index);
|
||||||
if (old_name.endsWith(".!qB") && !new_name_last.endsWith(".!qB")) {
|
if (old_name.endsWith(".!qB") && !new_name_last.endsWith(".!qB"))
|
||||||
new_name_last += ".!qB";
|
new_name_last += ".!qB";
|
||||||
}
|
|
||||||
QStringList path_items = old_name.split("/");
|
QStringList path_items = old_name.split("/");
|
||||||
path_items.removeLast();
|
path_items.removeLast();
|
||||||
path_items << new_name_last;
|
path_items << new_name_last;
|
||||||
|
@ -706,7 +724,7 @@ void PropertiesWidget::renameSelectedFile() {
|
||||||
QStringList path_items;
|
QStringList path_items;
|
||||||
path_items << index.data().toString();
|
path_items << index.data().toString();
|
||||||
QModelIndex parent = PropListModel->parent(index);
|
QModelIndex parent = PropListModel->parent(index);
|
||||||
while(parent.isValid()) {
|
while (parent.isValid()) {
|
||||||
path_items.prepend(parent.data().toString());
|
path_items.prepend(parent.data().toString());
|
||||||
parent = PropListModel->parent(parent);
|
parent = PropListModel->parent(parent);
|
||||||
}
|
}
|
||||||
|
@ -754,7 +772,7 @@ void PropertiesWidget::renameSelectedFile() {
|
||||||
// Remove old folder
|
// Remove old folder
|
||||||
const QDir old_folder(m_torrent->savePath(true) + "/" + old_path);
|
const QDir old_folder(m_torrent->savePath(true) + "/" + old_path);
|
||||||
int timeout = 10;
|
int timeout = 10;
|
||||||
while(!QDir().rmpath(old_folder.absolutePath()) && timeout > 0) {
|
while (!QDir().rmpath(old_folder.absolutePath()) && timeout > 0) {
|
||||||
// FIXME: We should not sleep here (freezes the UI for 1 second)
|
// FIXME: We should not sleep here (freezes the UI for 1 second)
|
||||||
Utils::Misc::msleep(100);
|
Utils::Misc::msleep(100);
|
||||||
--timeout;
|
--timeout;
|
||||||
|
@ -763,14 +781,16 @@ void PropertiesWidget::renameSelectedFile() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::openSelectedFile() {
|
void PropertiesWidget::openSelectedFile()
|
||||||
|
{
|
||||||
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0);
|
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0);
|
||||||
if (selectedIndexes.size() != 1)
|
if (selectedIndexes.size() != 1)
|
||||||
return;
|
return;
|
||||||
openDoubleClickedFile(selectedIndexes.first());
|
openDoubleClickedFile(selectedIndexes.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::askWebSeed() {
|
void PropertiesWidget::askWebSeed()
|
||||||
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
// Ask user for a new url seed
|
// Ask user for a new url seed
|
||||||
const QString url_seed = AutoExpandableDialog::getText(this, tr("New URL seed", "New HTTP source"),
|
const QString url_seed = AutoExpandableDialog::getText(this, tr("New URL seed", "New HTTP source"),
|
||||||
|
@ -790,7 +810,8 @@ void PropertiesWidget::askWebSeed() {
|
||||||
loadUrlSeeds();
|
loadUrlSeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::deleteSelectedUrlSeeds() {
|
void PropertiesWidget::deleteSelectedUrlSeeds()
|
||||||
|
{
|
||||||
const QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
|
const QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
|
||||||
if (selectedItems.isEmpty()) return;
|
if (selectedItems.isEmpty()) return;
|
||||||
|
|
||||||
|
@ -803,7 +824,8 @@ void PropertiesWidget::deleteSelectedUrlSeeds() {
|
||||||
loadUrlSeeds();
|
loadUrlSeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::copySelectedWebSeedsToClipboard() const {
|
void PropertiesWidget::copySelectedWebSeedsToClipboard() const
|
||||||
|
{
|
||||||
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems();
|
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems();
|
||||||
if (selected_items.isEmpty())
|
if (selected_items.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
@ -815,7 +837,8 @@ void PropertiesWidget::copySelectedWebSeedsToClipboard() const {
|
||||||
QApplication::clipboard()->setText(urls_to_copy.join("\n"));
|
QApplication::clipboard()->setText(urls_to_copy.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::editWebSeed() {
|
void PropertiesWidget::editWebSeed()
|
||||||
|
{
|
||||||
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems();
|
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems();
|
||||||
if (selected_items.size() != 1)
|
if (selected_items.size() != 1)
|
||||||
return;
|
return;
|
||||||
|
@ -841,7 +864,8 @@ void PropertiesWidget::editWebSeed() {
|
||||||
loadUrlSeeds();
|
loadUrlSeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PropertiesWidget::applyPriorities() {
|
bool PropertiesWidget::applyPriorities()
|
||||||
|
{
|
||||||
qDebug("Saving files priorities");
|
qDebug("Saving files priorities");
|
||||||
const QVector<int> priorities = PropListModel->model()->getFilePriorities();
|
const QVector<int> priorities = PropListModel->model()->getFilePriorities();
|
||||||
// Prioritize the files
|
// Prioritize the files
|
||||||
|
@ -850,17 +874,20 @@ bool PropertiesWidget::applyPriorities() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::filteredFilesChanged() {
|
void PropertiesWidget::filteredFilesChanged()
|
||||||
|
{
|
||||||
if (m_torrent)
|
if (m_torrent)
|
||||||
applyPriorities();
|
applyPriorities();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesWidget::filterText(const QString& filter) {
|
void PropertiesWidget::filterText(const QString &filter)
|
||||||
|
{
|
||||||
PropListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix));
|
PropListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix));
|
||||||
if (filter.isEmpty()) {
|
if (filter.isEmpty()) {
|
||||||
filesList->collapseAll();
|
filesList->collapseAll();
|
||||||
filesList->expand(PropListModel->index(0, 0));
|
filesList->expand(PropListModel->index(0, 0));
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
filesList->expandAll();
|
filesList->expandAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,8 @@ class QAction;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class PropertiesWidget : public QWidget, private Ui::PropertiesWidget {
|
class PropertiesWidget: public QWidget, private Ui::PropertiesWidget
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(PropertiesWidget)
|
Q_DISABLE_COPY(PropertiesWidget)
|
||||||
|
|
||||||
|
@ -63,16 +64,16 @@ public:
|
||||||
enum SlideState {REDUCED, VISIBLE};
|
enum SlideState {REDUCED, VISIBLE};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList);
|
PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList);
|
||||||
~PropertiesWidget();
|
~PropertiesWidget();
|
||||||
BitTorrent::TorrentHandle *getCurrentTorrent() const;
|
BitTorrent::TorrentHandle *getCurrentTorrent() const;
|
||||||
TrackerList* getTrackerList() const { return trackerList; }
|
TrackerList *getTrackerList() const { return trackerList; }
|
||||||
PeerListWidget* getPeerList() const { return peersList; }
|
PeerListWidget *getPeerList() const { return peersList; }
|
||||||
QTreeView* getFilesList() const { return filesList; }
|
QTreeView *getFilesList() const { return filesList; }
|
||||||
SpeedWidget* getSpeedWidget() const { return speedWidget; }
|
SpeedWidget *getSpeedWidget() const { return speedWidget; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QPushButton* getButtonFromIndex(int index);
|
QPushButton *getButtonFromIndex(int index);
|
||||||
bool applyPriorities();
|
bool applyPriorities();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
@ -83,8 +84,8 @@ protected slots:
|
||||||
void deleteSelectedUrlSeeds();
|
void deleteSelectedUrlSeeds();
|
||||||
void copySelectedWebSeedsToClipboard() const;
|
void copySelectedWebSeedsToClipboard() const;
|
||||||
void editWebSeed();
|
void editWebSeed();
|
||||||
void displayFilesListMenu(const QPoint& pos);
|
void displayFilesListMenu(const QPoint &pos);
|
||||||
void displayWebSeedListMenu(const QPoint& pos);
|
void displayWebSeedListMenu(const QPoint &pos);
|
||||||
void filteredFilesChanged();
|
void filteredFilesChanged();
|
||||||
void showPiecesDownloaded(bool show);
|
void showPiecesDownloaded(bool show);
|
||||||
void showPiecesAvailability(bool show);
|
void showPiecesAvailability(bool show);
|
||||||
|
@ -127,7 +128,7 @@ private:
|
||||||
QShortcut *openHotkeyFile;
|
QShortcut *openHotkeyFile;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void filterText(const QString& filter);
|
void filterText(const QString &filter);
|
||||||
void updateSavePath(BitTorrent::TorrentHandle *const torrent);
|
void updateSavePath(BitTorrent::TorrentHandle *const torrent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue