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>
36 #include <QPainterPath>
39 #include "base/bittorrent/torrent.h"
40 #include "base/bittorrent/torrentinfo.h"
41 #include "base/indexrange.h"
42 #include "base/path.h"
43 #include "base/utils/misc.h"
47 using ImageRange
= IndexRange
<int>;
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.
52 class PieceIndexToImagePos
55 PieceIndexToImagePos(const BitTorrent::TorrentInfo
&torrentInfo
, const QImage
&image
)
56 : m_bytesPerPixel
{((image
.width() > 0) && (torrentInfo
.totalSize() >= image
.width()))
57 ? torrentInfo
.totalSize() / image
.width() : -1}
58 , m_torrentInfo
{torrentInfo
}
60 if ((m_bytesPerPixel
> 0) && (m_bytesPerPixel
< 10))
61 qDebug() << "PieceIndexToImagePos: torrent size is too small for correct computaions."
62 << "Torrent size =" << torrentInfo
.totalSize() << "Image width = " << image
.width();
65 ImageRange
imagePos(const BitTorrent::TorrentInfo::PieceRange
&pieces
) const
67 if (m_bytesPerPixel
< 0)
70 // the type conversion is used to prevent integer overflow with torrents of 2+ GiB size
71 const qlonglong pieceLength
= m_torrentInfo
.pieceLength();
72 return makeInterval
<ImageRange::IndexType
>(
73 (pieces
.first() * pieceLength
) / m_bytesPerPixel
,
74 (pieces
.last() * pieceLength
+ m_torrentInfo
.pieceLength(pieces
.last()) - 1) / m_bytesPerPixel
);
77 int pieceIndex(int imagePos
) const
79 return m_bytesPerPixel
< 0 ? 0 : (imagePos
* m_bytesPerPixel
+ m_bytesPerPixel
/ 2) / m_torrentInfo
.pieceLength();
83 const qlonglong m_bytesPerPixel
; // how many bytes of the torrent are squeezed into a bar's pixel
84 const BitTorrent::TorrentInfo m_torrentInfo
;
87 class DetailedTooltipRenderer
90 DetailedTooltipRenderer(QString
&string
, const QString
&header
)
94 + uR
"(<table style="width
:100%; padding
: 3px
; vertical
-align
: middle
;">)";
97 ~DetailedTooltipRenderer()
99 m_string
+= u
"</table>";
102 void operator()(const QString
&size
, const Path
&path
)
104 m_string
+= uR
"(<tr><td style="white
-space
:nowrap
">)"
116 PiecesBar::PiecesBar(QWidget
*parent
)
120 setMouseTracking(true);
123 void PiecesBar::setTorrent(const BitTorrent::Torrent
*torrent
)
130 void PiecesBar::clear()
136 bool PiecesBar::event(QEvent
*e
)
138 if (e
->type() == QEvent::ToolTip
)
140 showToolTip(static_cast<QHelpEvent
*>(e
));
144 return base::event(e
);
147 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
148 void PiecesBar::enterEvent(QEnterEvent
*e
)
150 void PiecesBar::enterEvent(QEvent
*e
)
157 void PiecesBar::leaveEvent(QEvent
*e
)
160 m_highlightedRegion
= {};
161 requestImageUpdate();
165 void PiecesBar::mouseMoveEvent(QMouseEvent
*e
)
167 // if user pointed to a piece which is a part of a single large file,
168 // we highlight the space, occupied by this file
169 highlightFile(e
->pos().x() - borderWidth
);
170 base::mouseMoveEvent(e
);
173 void PiecesBar::paintEvent(QPaintEvent
*)
175 QPainter
painter(this);
176 QRect
imageRect(borderWidth
, borderWidth
, width() - 2 * borderWidth
, height() - 2 * borderWidth
);
177 if (m_image
.isNull())
179 painter
.setBrush(backgroundColor());
180 painter
.drawRect(imageRect
);
184 if (m_image
.width() != imageRect
.width())
185 updateImage(m_image
);
186 painter
.drawImage(imageRect
, m_image
);
189 if (!m_highlightedRegion
.isNull())
191 QColor highlightColor
{this->palette().color(QPalette::Active
, QPalette::Highlight
)};
192 highlightColor
.setAlphaF(0.35f
);
193 QRect targetHighlightRect
{m_highlightedRegion
.adjusted(borderWidth
, borderWidth
, borderWidth
, height() - 2 * borderWidth
)};
194 painter
.fillRect(targetHighlightRect
, highlightColor
);
198 border
.addRect(0, 0, width(), height());
199 painter
.setPen(borderColor());
200 painter
.drawPath(border
);
203 void PiecesBar::requestImageUpdate()
205 if (updateImage(m_image
))
209 QColor
PiecesBar::backgroundColor() const
211 return palette().color(QPalette::Base
);
214 QColor
PiecesBar::borderColor() const
216 return palette().color(QPalette::Dark
);
219 QColor
PiecesBar::pieceColor() const
221 return palette().color(QPalette::Highlight
);
224 QColor
PiecesBar::colorBoxBorderColor() const
226 return palette().color(QPalette::ToolTipText
);
229 const QVector
<QRgb
> &PiecesBar::pieceColors() const
231 return m_pieceColors
;
234 QRgb
PiecesBar::mixTwoColors(QRgb rgb1
, QRgb rgb2
, float ratio
)
237 int g1
= qGreen(rgb1
);
238 int b1
= qBlue(rgb1
);
241 int g2
= qGreen(rgb2
);
242 int b2
= qBlue(rgb2
);
244 float ratioN
= 1.0f
- ratio
;
245 int r
= (r1
* ratioN
) + (r2
* ratio
);
246 int g
= (g1
* ratioN
) + (g2
* ratio
);
247 int b
= (b1
* ratioN
) + (b2
* ratio
);
249 return qRgb(r
, g
, b
);
252 void PiecesBar::showToolTip(const QHelpEvent
*e
)
259 const bool showDetailedInformation
= QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier
);
260 if (showDetailedInformation
&& m_torrent
->hasMetadata())
262 const BitTorrent::TorrentInfo torrentInfo
= m_torrent
->info();
263 const int imagePos
= e
->pos().x() - borderWidth
;
264 if ((imagePos
>= 0) && (imagePos
< m_image
.width()))
266 const PieceIndexToImagePos transform
{torrentInfo
, m_image
};
267 const int pieceIndex
= transform
.pieceIndex(imagePos
);
268 const QVector
<int> fileIndexes
= torrentInfo
.fileIndicesForPiece(pieceIndex
);
270 QString tooltipTitle
;
271 if (fileIndexes
.count() > 1)
272 tooltipTitle
= tr("Files in this piece:");
273 else if (torrentInfo
.fileSize(fileIndexes
.front()) == torrentInfo
.pieceLength(pieceIndex
))
274 tooltipTitle
= tr("File in this piece:");
276 tooltipTitle
= tr("File in these pieces:");
278 toolTipText
.reserve(fileIndexes
.size() * 128);
279 toolTipText
+= u
"<html><body>";
281 DetailedTooltipRenderer renderer
{toolTipText
, tooltipTitle
};
283 for (const int index
: fileIndexes
)
285 const Path filePath
= m_torrent
->filePath(index
);
286 renderer(Utils::Misc::friendlyUnit(torrentInfo
.fileSize(index
)), filePath
);
288 toolTipText
+= u
"</body></html>";
293 toolTipText
+= simpleToolTipText();
294 if (showDetailedInformation
) // metadata are not available at this point
295 toolTipText
+= u
'\n' + tr("Wait until metadata become available to see detailed information");
297 toolTipText
+= u
'\n' + tr("Hold Shift key for detailed information");
300 QToolTip::showText(e
->globalPos(), toolTipText
, this);
303 void PiecesBar::highlightFile(int imagePos
)
305 if (!m_torrent
|| !m_torrent
->hasMetadata() || (imagePos
< 0) || (imagePos
>= m_image
.width()))
308 const BitTorrent::TorrentInfo torrentInfo
= m_torrent
->info();
309 PieceIndexToImagePos transform
{torrentInfo
, m_image
};
311 int pieceIndex
= transform
.pieceIndex(imagePos
);
312 QVector
<int> fileIndices
{torrentInfo
.fileIndicesForPiece(pieceIndex
)};
313 if (fileIndices
.count() == 1)
315 BitTorrent::TorrentInfo::PieceRange filePieces
= torrentInfo
.filePieces(fileIndices
.first());
317 ImageRange imageRange
= transform
.imagePos(filePieces
);
318 QRect newHighlightedRegion
{imageRange
.first(), 0, imageRange
.size(), m_image
.height()};
319 if (newHighlightedRegion
!= m_highlightedRegion
)
321 m_highlightedRegion
= newHighlightedRegion
;
325 else if (!m_highlightedRegion
.isEmpty())
327 m_highlightedRegion
= {};
332 void PiecesBar::updatePieceColors()
334 m_pieceColors
= QVector
<QRgb
>(256);
335 for (int i
= 0; i
< 256; ++i
)
337 float ratio
= (i
/ 255.0);
338 m_pieceColors
[i
] = mixTwoColors(backgroundColor().rgb(), pieceColor().rgb(), ratio
);