Don't use 'else' after return/break
[qBittorrent.git] / src / gui / properties / piecesbar.cpp
blob2836923ca3b82c9c8829d2d6b3893be34fc37580
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2016 Eugene Shalygin
4 * Copyright (C) 2006 Christophe Dumez
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
30 #include "piecesbar.h"
32 #include <QApplication>
33 #include <QDebug>
34 #include <QHelpEvent>
35 #include <QPainter>
36 #include <QTextStream>
37 #include <QToolTip>
39 #include "base/bittorrent/torrenthandle.h"
40 #include "base/utils/misc.h"
42 namespace
44 using ImageRange = IndexRange<int>;
46 // Computes approximate mapping from image scale (measured in pixels) onto the torrent contents scale (in pieces)
47 // However, taking the size of a screen to be ~ 1000 px and the torrent size larger than 10 MiB, the pointing error
48 // is well below 0.5 px and thus is negligible.
49 class PieceIndexToImagePos
51 public:
52 PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
53 : m_bytesPerPixel {((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
54 ? torrentInfo.totalSize() / image.width() : -1}
55 , m_torrentInfo {torrentInfo}
57 if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
58 qDebug() << "PieceIndexToImagePos: torrent size is too small for correct computaions."
59 << "Torrent size =" << torrentInfo.totalSize() << "Image width = " << image.width();
62 ImageRange imagePos(const BitTorrent::TorrentInfo::PieceRange &pieces) const
64 if (m_bytesPerPixel < 0)
65 return {0, 0};
67 // the type conversion is used to prevent integer overflow with torrents of 2+ GiB size
68 const qlonglong pieceLength = m_torrentInfo.pieceLength();
69 return makeInterval<ImageRange::IndexType>(
70 (pieces.first() * pieceLength) / m_bytesPerPixel,
71 (pieces.last() * pieceLength + m_torrentInfo.pieceLength(pieces.last()) - 1) / m_bytesPerPixel);
74 int pieceIndex(int imagePos) const
76 return m_bytesPerPixel < 0 ? 0 : (imagePos * m_bytesPerPixel + m_bytesPerPixel / 2) / m_torrentInfo.pieceLength();
79 private:
80 const qlonglong m_bytesPerPixel; // how many bytes of the torrent are squeezed into a bar's pixel
81 const BitTorrent::TorrentInfo m_torrentInfo;
84 class DetailedTooltipRenderer
86 public:
87 DetailedTooltipRenderer(QTextStream &stream, const QString &header)
88 : m_stream(stream)
90 m_stream << header
91 << R"(<table style="width:100%; padding: 3px; vertical-align: middle;">)";
94 ~DetailedTooltipRenderer()
96 m_stream << "</table>";
99 void operator()(const QString &size, const QString &path)
101 m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path << "</td></tr>";
104 private:
105 QTextStream &m_stream;
109 PiecesBar::PiecesBar(QWidget *parent)
110 : QWidget {parent}
111 , m_torrent {nullptr}
112 , m_borderColor {palette().color(QPalette::Dark)}
113 , m_bgColor {Qt::white}
114 , m_pieceColor {Qt::blue}
115 , m_hovered {false}
117 updatePieceColors();
118 setMouseTracking(true);
121 void PiecesBar::setTorrent(BitTorrent::TorrentHandle *torrent)
123 m_torrent = torrent;
124 if (!m_torrent)
125 clear();
128 void PiecesBar::clear()
130 m_image = QImage();
131 update();
134 void PiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete)
136 m_bgColor = background;
137 m_borderColor = border;
138 m_pieceColor = complete;
140 updatePieceColors();
141 requestImageUpdate();
144 bool PiecesBar::event(QEvent *e)
146 if (e->type() == QEvent::ToolTip) {
147 showToolTip(static_cast<QHelpEvent *>(e));
148 return true;
151 return base::event(e);
154 void PiecesBar::enterEvent(QEvent *e)
156 m_hovered = true;
157 base::enterEvent(e);
160 void PiecesBar::leaveEvent(QEvent *e)
162 m_hovered = false;
163 m_highlitedRegion = QRect();
164 requestImageUpdate();
165 base::leaveEvent(e);
168 void PiecesBar::mouseMoveEvent(QMouseEvent *e)
170 // if user pointed to a piece which is a part of a single large file,
171 // we highlight the space, occupied by this file
172 highlightFile(e->pos().x() - borderWidth);
173 base::mouseMoveEvent(e);
176 void PiecesBar::paintEvent(QPaintEvent *)
178 QPainter painter(this);
179 QRect imageRect(borderWidth, borderWidth, width() - 2 * borderWidth, height() - 2 * borderWidth);
180 if (m_image.isNull()) {
181 painter.setBrush(Qt::white);
182 painter.drawRect(imageRect);
184 else {
185 if (m_image.width() != imageRect.width())
186 updateImage(m_image);
187 painter.drawImage(imageRect, m_image);
190 if (!m_highlitedRegion.isNull()) {
191 QColor highlightColor {this->palette().color(QPalette::Active, QPalette::Highlight)};
192 highlightColor.setAlphaF(0.35);
193 QRect targetHighlightRect {m_highlitedRegion.adjusted(borderWidth, borderWidth, borderWidth, height() - 2 * borderWidth)};
194 painter.fillRect(targetHighlightRect, highlightColor);
197 QPainterPath border;
198 border.addRect(0, 0, width(), height());
199 painter.setPen(m_borderColor);
200 painter.drawPath(border);
203 void PiecesBar::requestImageUpdate()
205 if (updateImage(m_image))
206 update();
209 QColor PiecesBar::backgroundColor() const
211 return m_bgColor;
214 QColor PiecesBar::borderColor() const
216 return m_borderColor;
219 QColor PiecesBar::pieceColor() const
221 return m_pieceColor;
224 const QVector<QRgb> &PiecesBar::pieceColors() const
226 return m_pieceColors;
229 QRgb PiecesBar::mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio)
231 int r1 = qRed(rgb1);
232 int g1 = qGreen(rgb1);
233 int b1 = qBlue(rgb1);
235 int r2 = qRed(rgb2);
236 int g2 = qGreen(rgb2);
237 int b2 = qBlue(rgb2);
239 float ratioN = 1.0f - ratio;
240 int r = (r1 * ratioN) + (r2 * ratio);
241 int g = (g1 * ratioN) + (g2 * ratio);
242 int b = (b1 * ratioN) + (b2 * ratio);
244 return qRgb(r, g, b);
247 void PiecesBar::showToolTip(const QHelpEvent *e)
249 if (!m_torrent)
250 return;
252 QString toolTipText;
253 QTextStream stream(&toolTipText, QIODevice::WriteOnly);
254 const bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
255 if (showDetailedInformation && m_torrent->hasMetadata()) {
256 const int imagePos = e->pos().x() - borderWidth;
257 if ((imagePos >=0) && (imagePos < m_image.width())) {
258 stream << "<html><body>";
259 PieceIndexToImagePos transform {m_torrent->info(), m_image};
260 int pieceIndex = transform.pieceIndex(imagePos);
261 const QVector<int> files {m_torrent->info().fileIndicesForPiece(pieceIndex)};
263 QString tooltipTitle;
264 if (files.count() > 1) {
265 tooltipTitle = tr("Files in this piece:");
267 else {
268 if (m_torrent->info().fileSize(files.front()) == m_torrent->info().pieceLength(pieceIndex))
269 tooltipTitle = tr("File in this piece");
270 else
271 tooltipTitle = tr("File in these pieces");
274 DetailedTooltipRenderer renderer(stream, tooltipTitle);
276 const bool isFileNameCorrectionNeeded = this->isFileNameCorrectionNeeded();
277 for (int f : files) {
278 QString filePath {m_torrent->info().filePath(f)};
279 if (isFileNameCorrectionNeeded)
280 filePath.replace(QLatin1String("/.unwanted"), QString());
282 renderer(Utils::Misc::friendlyUnit(m_torrent->info().fileSize(f)), filePath);
284 stream << "</body></html>";
287 else {
288 stream << simpleToolTipText();
289 if (showDetailedInformation) // metadata are not available at this point
290 stream << '\n' << tr("Wait until metadata become available to see detailed information");
291 else
292 stream << '\n' << tr("Hold Shift key for detailed information");
295 stream.flush();
297 QToolTip::showText(e->globalPos(), toolTipText, this);
300 void PiecesBar::highlightFile(int imagePos)
302 if (!m_torrent || !m_torrent->hasMetadata() || (imagePos < 0) || (imagePos >= m_image.width()))
303 return;
305 PieceIndexToImagePos transform {m_torrent->info(), m_image};
307 int pieceIndex = transform.pieceIndex(imagePos);
308 QVector<int> fileIndices {m_torrent->info().fileIndicesForPiece(pieceIndex)};
309 if (fileIndices.count() == 1) {
310 BitTorrent::TorrentInfo::PieceRange filePieces = m_torrent->info().filePieces(fileIndices.first());
312 ImageRange imageRange = transform.imagePos(filePieces);
313 QRect newHighlitedRegion {imageRange.first(), 0, imageRange.size(), m_image.height()};
314 if (newHighlitedRegion != m_highlitedRegion) {
315 m_highlitedRegion = newHighlitedRegion;
316 update();
319 else if (!m_highlitedRegion.isEmpty()) {
320 m_highlitedRegion = QRect();
321 update();
325 void PiecesBar::updatePieceColors()
327 m_pieceColors = QVector<QRgb>(256);
328 for (int i = 0; i < 256; ++i) {
329 float ratio = (i / 255.0);
330 m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), m_pieceColor.rgb(), ratio);
334 bool PiecesBar::isFileNameCorrectionNeeded() const
336 return false;