1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-30 19:46:56 +00:00
transmission/qt/TrackerDelegate.cc

343 lines
12 KiB
C++

/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#include <QAbstractTextDocumentLayout>
#include <QApplication>
#include <QPainter>
#include <QPixmap>
#include <QTextDocument>
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h>
#include "FaviconCache.h"
#include "Formatter.h"
#include "Torrent.h"
#include "TrackerDelegate.h"
#include "TrackerModel.h"
#include "Utils.h"
/***
****
***/
namespace
{
const int mySpacing = 6;
const QSize myMargin (10, 10);
class ItemLayout
{
private:
QTextDocument myTextDocument;
public:
QRect iconRect;
QRect textRect;
public:
ItemLayout(const QString& text, bool suppressColors, Qt::LayoutDirection direction,
const QPoint& topLeft, int width);
QSize size () const
{
return (iconRect | textRect).size ();
}
QAbstractTextDocumentLayout * textLayout () const
{
return myTextDocument.documentLayout ();
}
};
ItemLayout::ItemLayout(const QString& text, bool suppressColors, Qt::LayoutDirection direction,
const QPoint& topLeft, int width)
{
const QStyle * style (qApp->style ());
const QSize iconSize = FaviconCache::getIconSize ();
QRect baseRect (topLeft, QSize (width, 0));
iconRect = style->alignedRect (direction, Qt::AlignLeft | Qt::AlignTop, iconSize, baseRect);
Utils::narrowRect (baseRect, iconSize.width () + mySpacing, 0, direction);
myTextDocument.setDocumentMargin (0);
myTextDocument.setTextWidth (baseRect.width ());
QTextOption textOption;
textOption.setTextDirection (direction);
if (suppressColors)
textOption.setFlags (QTextOption::SuppressColors);
myTextDocument.setDefaultTextOption (textOption);
myTextDocument.setHtml (text);
textRect = baseRect;
textRect.setSize (myTextDocument.size ().toSize ());
}
}
QSize
TrackerDelegate::margin (const QStyle& style) const
{
Q_UNUSED (style);
return myMargin;
}
/***
****
***/
QSize
TrackerDelegate::sizeHint (const QStyleOptionViewItem & option,
const TrackerInfo & info) const
{
const ItemLayout layout (getText (info), true, option.direction, QPoint (0, 0), option.rect.width () - myMargin.width () * 2);
return layout.size () + myMargin * 2;
}
QSize
TrackerDelegate::sizeHint (const QStyleOptionViewItem & option,
const QModelIndex & index) const
{
const TrackerInfo trackerInfo = index.data (TrackerModel::TrackerRole).value<TrackerInfo>();
return sizeHint (option, trackerInfo);
}
void
TrackerDelegate::paint (QPainter * painter,
const QStyleOptionViewItem & option,
const QModelIndex & index) const
{
const TrackerInfo trackerInfo = index.data (TrackerModel::TrackerRole).value<TrackerInfo>();
painter->save();
painter->setClipRect (option.rect);
drawBackground (painter, option, index);
drawTracker (painter, option, trackerInfo);
drawFocus(painter, option, option.rect);
painter->restore();
}
void
TrackerDelegate::drawTracker (QPainter * painter,
const QStyleOptionViewItem & option,
const TrackerInfo & inf) const
{
const bool isItemSelected ((option.state & QStyle::State_Selected) != 0);
const bool isItemEnabled ((option.state & QStyle::State_Enabled) != 0);
const bool isItemActive ((option.state & QStyle::State_Active) != 0);
QIcon trackerIcon (inf.st.getFavicon());
const QRect contentRect (option.rect.adjusted (myMargin.width (), myMargin.height (), -myMargin.width (), -myMargin.height ()));
const ItemLayout layout (getText (inf), isItemSelected, option.direction, contentRect.topLeft (), contentRect.width ());
painter->save();
if (isItemSelected)
{
QPalette::ColorGroup cg = isItemEnabled ? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !isItemActive)
cg = QPalette::Inactive;
painter->fillRect (option.rect, option.palette.brush (cg, QPalette::Highlight));
}
trackerIcon.paint (painter, layout.iconRect, Qt::AlignCenter, isItemSelected ? QIcon::Selected : QIcon::Normal, QIcon::On);
QAbstractTextDocumentLayout::PaintContext paintContext;
paintContext.clip = layout.textRect.translated (-layout.textRect.topLeft ());
paintContext.palette.setColor (QPalette::Text, option.palette.color (isItemSelected ? QPalette::HighlightedText : QPalette::Text));
painter->translate (layout.textRect.topLeft());
layout.textLayout ()->draw (painter, paintContext);
painter->restore();
}
void
TrackerDelegate::setShowMore (bool b)
{
myShowMore = b;
}
namespace
{
QString timeToStringRounded (int seconds)
{
if (seconds > 60)
seconds -= (seconds % 60);
return Formatter::timeToString (seconds);
}
}
QString
TrackerDelegate::getText (const TrackerInfo& inf) const
{
QString key;
QString str;
const time_t now (time (0));
const QString err_markup_begin = QLatin1String ("<span style=\"color:red\">");
const QString err_markup_end = QLatin1String ("</span>");
const QString timeout_markup_begin = QLatin1String ("<span style=\"color:#224466\">");
const QString timeout_markup_end = QLatin1String ("</span>");
const QString success_markup_begin = QLatin1String ("<span style=\"color:#008B00\">");
const QString success_markup_end = QLatin1String ("</span>");
// hostname
str += inf.st.isBackup ? QLatin1String ("<i>") : QLatin1String ("<b>");
char * host = NULL;
int port = 0;
tr_urlParse (inf.st.announce.toUtf8().constData(), -1, NULL, &host, &port, NULL);
str += QString::fromLatin1 ("%1:%2").arg (QString::fromUtf8 (host)).arg (port);
tr_free (host);
if (!key.isEmpty()) str += QLatin1String (" - ") + key;
str += inf.st.isBackup ? QLatin1String ("</i>") : QLatin1String ("</b>");
// announce & scrape info
if (!inf.st.isBackup)
{
if (inf.st.hasAnnounced && inf.st.announceState != TR_TRACKER_INACTIVE)
{
const QString tstr (timeToStringRounded (now - inf.st.lastAnnounceTime));
str += QLatin1String ("<br/>\n");
if (inf.st.lastAnnounceSucceeded)
{
//: %1 and %2 are replaced with HTML markup, %3 is duration
str += tr ("Got a list of%1 %Ln peer(s)%2 %3 ago", 0, inf.st.lastAnnouncePeerCount)
.arg (success_markup_begin)
.arg (success_markup_end)
.arg (tstr);
}
else if (inf.st.lastAnnounceTimedOut)
{
//: %1 and %2 are replaced with HTML markup, %3 is duration
str += tr ("Peer list request %1timed out%2 %3 ago; will retry")
.arg (timeout_markup_begin)
.arg (timeout_markup_end)
.arg (tstr);
}
else
{
//: %1 and %3 are replaced with HTML markup, %2 is error message, %4 is duration
str += tr ("Got an error %1\"%2\"%3 %4 ago")
.arg (err_markup_begin)
.arg (inf.st.lastAnnounceResult)
.arg (err_markup_end)
.arg (tstr);
}
}
switch (inf.st.announceState)
{
case TR_TRACKER_INACTIVE:
str += QLatin1String ("<br/>\n");
str += tr ("No updates scheduled");
break;
case TR_TRACKER_WAITING:
{
const QString tstr (timeToStringRounded (inf.st.nextAnnounceTime - now));
str += QLatin1String ("<br/>\n");
//: %1 is duration
str += tr ("Asking for more peers in %1").arg (tstr);
break;
}
case TR_TRACKER_QUEUED:
str += QLatin1String ("<br/>\n");
str += tr ("Queued to ask for more peers");
break;
case TR_TRACKER_ACTIVE: {
const QString tstr (timeToStringRounded (now - inf.st.lastAnnounceStartTime));
str += QLatin1String ("<br/>\n");
//: %1 is duration
str += tr ("Asking for more peers now... <small>%1</small>").arg (tstr);
break;
}
}
if (myShowMore)
{
if (inf.st.hasScraped)
{
str += QLatin1String ("<br/>\n");
const QString tstr (timeToStringRounded (now - inf.st.lastScrapeTime));
if (inf.st.lastScrapeSucceeded)
{
if (inf.st.seederCount >= 0 && inf.st.leecherCount >= 0)
{
//: First part of phrase "Tracker had ... seeder(s) and ... leecher(s) ... ago";
//: %1 and %2 are replaced with HTML markup
str += tr ("Tracker had%1 %Ln seeder(s)%2", 0, inf.st.seederCount)
.arg (success_markup_begin)
.arg (success_markup_end);
//: Second part of phrase "Tracker had ... seeder(s) and ... leecher(s) ... ago";
//: %1 and %2 are replaced with HTML markup, %3 is duration;
//: notice that leading space (before "and") is included here
str += tr (" and%1 %Ln leecher(s)%2 %3 ago", 0, inf.st.leecherCount)
.arg (success_markup_begin)
.arg (success_markup_end)
.arg (tstr);
}
else
{
//: %1 and %2 are replaced with HTML markup, %3 is duration
str += tr ("Tracker had %1no information%2 on peer counts %3 ago")
.arg (success_markup_begin)
.arg (success_markup_end)
.arg (tstr);
}
}
else
{
//: %1 and %3 are replaced with HTML markup, %2 is error message, %4 is duration
str += tr ("Got a scrape error %1\"%2\"%3 %4 ago")
.arg (err_markup_begin)
.arg (inf.st.lastScrapeResult)
.arg (err_markup_end)
.arg (tstr);
}
}
switch (inf.st.scrapeState)
{
case TR_TRACKER_INACTIVE:
break;
case TR_TRACKER_WAITING:
{
str += QLatin1String ("<br/>\n");
const QString tstr (timeToStringRounded (inf.st.nextScrapeTime - now));
//: %1 is duration
str += tr ("Asking for peer counts in %1").arg (tstr);
break;
}
case TR_TRACKER_QUEUED:
{
str += QLatin1String ("<br/>\n");
str += tr ("Queued to ask for peer counts");
break;
}
case TR_TRACKER_ACTIVE:
{
str += QLatin1String ("<br/>\n");
const QString tstr (timeToStringRounded (now - inf.st.lastScrapeStartTime));
//: %1 is duration
str += tr ("Asking for peer counts now... <small>%1</small>").arg (tstr);
break;
}
}
}
}
return str;
}