qBittorrent
piecesbar.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2016 Eugene Shalygin
4  * Copyright (C) 2006 Christophe Dumez
5  *
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.
10  *
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.
15  *
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.
19  *
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.
28  */
29 
30 #include "piecesbar.h"
31 
32 #include <QApplication>
33 #include <QDebug>
34 #include <QHelpEvent>
35 #include <QPainter>
36 #include <QPainterPath>
37 #include <QTextStream>
38 #include <QToolTip>
39 
40 #include "base/indexrange.h"
43 #include "base/utils/misc.h"
44 
45 namespace
46 {
48 
49  // Computes approximate mapping from image scale (measured in pixels) onto the torrent contents scale (in pieces)
50  // However, taking the size of a screen to be ~ 1000 px and the torrent size larger than 10 MiB, the pointing error
51  // is well below 0.5 px and thus is negligible.
53  {
54  public:
55  PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
56  : m_bytesPerPixel
57  {((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
58  ? torrentInfo.totalSize() / image.width() : -1}
59  , m_torrentInfo {torrentInfo}
60  {
61  if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
62  qDebug() << "PieceIndexToImagePos: torrent size is too small for correct computaions."
63  << "Torrent size =" << torrentInfo.totalSize() << "Image width = " << image.width();
64  }
65 
67  {
68  if (m_bytesPerPixel < 0)
69  return {0, 0};
70 
71  // the type conversion is used to prevent integer overflow with torrents of 2+ GiB size
72  const qlonglong pieceLength = m_torrentInfo.pieceLength();
73  return makeInterval<ImageRange::IndexType>(
74  (pieces.first() * pieceLength) / m_bytesPerPixel,
75  (pieces.last() * pieceLength + m_torrentInfo.pieceLength(pieces.last()) - 1) / m_bytesPerPixel);
76  }
77 
78  int pieceIndex(int imagePos) const
79  {
80  return m_bytesPerPixel < 0 ? 0 : (imagePos * m_bytesPerPixel + m_bytesPerPixel / 2) / m_torrentInfo.pieceLength();
81  }
82 
83  private:
84  const qlonglong m_bytesPerPixel; // how many bytes of the torrent are squeezed into a bar's pixel
86  };
87 
89  {
90  public:
91  DetailedTooltipRenderer(QTextStream &stream, const QString &header)
92  : m_stream(stream)
93  {
94  m_stream << header
95  << R"(<table style="width:100%; padding: 3px; vertical-align: middle;">)";
96  }
97 
99  {
100  m_stream << "</table>";
101  }
102 
103  void operator()(const QString &size, const QString &path)
104  {
105  m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path << "</td></tr>";
106  }
107 
108  private:
109  QTextStream &m_stream;
110  };
111 }
112 
113 PiecesBar::PiecesBar(QWidget *parent)
114  : QWidget {parent}
115 {
117  setMouseTracking(true);
118 }
119 
121 {
122  m_torrent = torrent;
123  if (!m_torrent)
124  clear();
125 }
126 
128 {
129  m_image = QImage();
130  update();
131 }
132 
133 bool PiecesBar::event(QEvent *e)
134 {
135  if (e->type() == QEvent::ToolTip)
136  {
137  showToolTip(static_cast<QHelpEvent *>(e));
138  return true;
139  }
140 
141  return base::event(e);
142 }
143 
144 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
145 void PiecesBar::enterEvent(QEnterEvent *e)
146 #else
147 void PiecesBar::enterEvent(QEvent *e)
148 #endif
149 {
150  m_hovered = true;
151  base::enterEvent(e);
152 }
153 
155 {
156  m_hovered = false;
157  m_highlightedRegion = {};
159  base::leaveEvent(e);
160 }
161 
162 void PiecesBar::mouseMoveEvent(QMouseEvent *e)
163 {
164  // if user pointed to a piece which is a part of a single large file,
165  // we highlight the space, occupied by this file
166  highlightFile(e->pos().x() - borderWidth);
167  base::mouseMoveEvent(e);
168 }
169 
170 void PiecesBar::paintEvent(QPaintEvent *)
171 {
172  QPainter painter(this);
173  QRect imageRect(borderWidth, borderWidth, width() - 2 * borderWidth, height() - 2 * borderWidth);
174  if (m_image.isNull())
175  {
176  painter.setBrush(backgroundColor());
177  painter.drawRect(imageRect);
178  }
179  else
180  {
181  if (m_image.width() != imageRect.width())
183  painter.drawImage(imageRect, m_image);
184  }
185 
186  if (!m_highlightedRegion.isNull())
187  {
188  QColor highlightColor {this->palette().color(QPalette::Active, QPalette::Highlight)};
189  highlightColor.setAlphaF(0.35f);
190  QRect targetHighlightRect {m_highlightedRegion.adjusted(borderWidth, borderWidth, borderWidth, height() - 2 * borderWidth)};
191  painter.fillRect(targetHighlightRect, highlightColor);
192  }
193 
194  QPainterPath border;
195  border.addRect(0, 0, width(), height());
196  painter.setPen(borderColor());
197  painter.drawPath(border);
198 }
199 
201 {
202  if (updateImage(m_image))
203  update();
204 }
205 
207 {
208  return palette().color(QPalette::Base);
209 }
210 
212 {
213  return palette().color(QPalette::Dark);
214 }
215 
216 QColor PiecesBar::pieceColor() const
217 {
218  return palette().color(QPalette::Highlight);
219 }
220 
222 {
223  return palette().color(QPalette::ToolTipText);
224 }
225 
226 const QVector<QRgb> &PiecesBar::pieceColors() const
227 {
228  return m_pieceColors;
229 }
230 
231 QRgb PiecesBar::mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio)
232 {
233  int r1 = qRed(rgb1);
234  int g1 = qGreen(rgb1);
235  int b1 = qBlue(rgb1);
236 
237  int r2 = qRed(rgb2);
238  int g2 = qGreen(rgb2);
239  int b2 = qBlue(rgb2);
240 
241  float ratioN = 1.0f - ratio;
242  int r = (r1 * ratioN) + (r2 * ratio);
243  int g = (g1 * ratioN) + (g2 * ratio);
244  int b = (b1 * ratioN) + (b2 * ratio);
245 
246  return qRgb(r, g, b);
247 }
248 
249 void PiecesBar::showToolTip(const QHelpEvent *e)
250 {
251  if (!m_torrent)
252  return;
253 
254  QString toolTipText;
255  QTextStream stream(&toolTipText, QIODevice::WriteOnly);
256  const bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
257  if (showDetailedInformation && m_torrent->hasMetadata())
258  {
259  const BitTorrent::TorrentInfo torrentInfo = m_torrent->info();
260  const int imagePos = e->pos().x() - borderWidth;
261  if ((imagePos >=0) && (imagePos < m_image.width()))
262  {
263  stream << "<html><body>";
264  PieceIndexToImagePos transform {torrentInfo, m_image};
265  int pieceIndex = transform.pieceIndex(imagePos);
266  const QVector<int> files {torrentInfo.fileIndicesForPiece(pieceIndex)};
267 
268  QString tooltipTitle;
269  if (files.count() > 1)
270  {
271  tooltipTitle = tr("Files in this piece:");
272  }
273  else
274  {
275  if (torrentInfo.fileSize(files.front()) == torrentInfo.pieceLength(pieceIndex))
276  tooltipTitle = tr("File in this piece");
277  else
278  tooltipTitle = tr("File in these pieces");
279  }
280 
281  DetailedTooltipRenderer renderer(stream, tooltipTitle);
282 
283  for (int f : files)
284  {
285  const QString filePath {torrentInfo.filePath(f)};
286  renderer(Utils::Misc::friendlyUnit(torrentInfo.fileSize(f)), filePath);
287  }
288  stream << "</body></html>";
289  }
290  }
291  else
292  {
293  stream << simpleToolTipText();
294  if (showDetailedInformation) // metadata are not available at this point
295  stream << '\n' << tr("Wait until metadata become available to see detailed information");
296  else
297  stream << '\n' << tr("Hold Shift key for detailed information");
298  }
299 
300  stream.flush();
301 
302  QToolTip::showText(e->globalPos(), toolTipText, this);
303 }
304 
305 void PiecesBar::highlightFile(int imagePos)
306 {
307  if (!m_torrent || !m_torrent->hasMetadata() || (imagePos < 0) || (imagePos >= m_image.width()))
308  return;
309 
310  const BitTorrent::TorrentInfo torrentInfo = m_torrent->info();
311  PieceIndexToImagePos transform {torrentInfo, m_image};
312 
313  int pieceIndex = transform.pieceIndex(imagePos);
314  QVector<int> fileIndices {torrentInfo.fileIndicesForPiece(pieceIndex)};
315  if (fileIndices.count() == 1)
316  {
317  BitTorrent::TorrentInfo::PieceRange filePieces = torrentInfo.filePieces(fileIndices.first());
318 
319  ImageRange imageRange = transform.imagePos(filePieces);
320  QRect newHighlightedRegion {imageRange.first(), 0, imageRange.size(), m_image.height()};
321  if (newHighlightedRegion != m_highlightedRegion)
322  {
323  m_highlightedRegion = newHighlightedRegion;
324  update();
325  }
326  }
327  else if (!m_highlightedRegion.isEmpty())
328  {
329  m_highlightedRegion = {};
330  update();
331  }
332 }
333 
335 {
336  m_pieceColors = QVector<QRgb>(256);
337  for (int i = 0; i < 256; ++i)
338  {
339  float ratio = (i / 255.0);
340  m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), pieceColor().rgb(), ratio);
341  }
342 }
virtual TorrentInfo info() const =0
virtual bool hasMetadata() const =0
qlonglong totalSize() const
QString filePath(int index) const
PieceRange filePieces(const QString &file) const
qlonglong fileSize(int index) const
QVector< int > fileIndicesForPiece(int pieceIndex) const
constexpr IndexType last() const
Definition: indexrange.h:156
constexpr IndexType first() const
Definition: indexrange.h:151
static QRgb mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio)
Definition: piecesbar.cpp:231
virtual bool event(QEvent *e) override
Definition: piecesbar.cpp:133
QColor colorBoxBorderColor() const
Definition: piecesbar.cpp:221
QColor pieceColor() const
Definition: piecesbar.cpp:216
QColor backgroundColor() const
Definition: piecesbar.cpp:206
void requestImageUpdate()
Definition: piecesbar.cpp:200
void mouseMoveEvent(QMouseEvent *e) override
Definition: piecesbar.cpp:162
const BitTorrent::Torrent * m_torrent
Definition: piecesbar.h:94
virtual bool updateImage(QImage &image)=0
QImage m_image
Definition: piecesbar.h:95
void updatePieceColors()
Definition: piecesbar.cpp:334
PiecesBar(QWidget *parent=nullptr)
Definition: piecesbar.cpp:113
void enterEvent(QEnterEvent *e) override
Definition: piecesbar.cpp:145
void highlightFile(int imagePos)
Definition: piecesbar.cpp:305
QVector< QRgb > m_pieceColors
Definition: piecesbar.h:97
static constexpr int borderWidth
Definition: piecesbar.h:81
void showToolTip(const QHelpEvent *)
Definition: piecesbar.cpp:249
void paintEvent(QPaintEvent *e) override
Definition: piecesbar.cpp:170
void setTorrent(const BitTorrent::Torrent *torrent)
Definition: piecesbar.cpp:120
void leaveEvent(QEvent *e) override
Definition: piecesbar.cpp:154
virtual void clear()
Definition: piecesbar.cpp:127
virtual QString simpleToolTipText() const =0
QRect m_highlightedRegion
Definition: piecesbar.h:99
const QVector< QRgb > & pieceColors() const
Definition: piecesbar.cpp:226
QColor borderColor() const
Definition: piecesbar.cpp:211
bool m_hovered
Definition: piecesbar.h:98
DetailedTooltipRenderer(QTextStream &stream, const QString &header)
Definition: piecesbar.cpp:91
void operator()(const QString &size, const QString &path)
Definition: piecesbar.cpp:103
ImageRange imagePos(const BitTorrent::TorrentInfo::PieceRange &pieces) const
Definition: piecesbar.cpp:66
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
Definition: piecesbar.cpp:55
flag icons free of to any person obtaining a copy of this software and associated documentation files(the "Software")
QString friendlyUnit(qint64 bytes, bool isSpeed=false)
Definition: misc.cpp:261
void f()
Definition: test2.c:1