/* * 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. * */ #include #include #include #include #include #include #include #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(); return sizeHint (option, trackerInfo); } void TrackerDelegate::paint (QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { const TrackerInfo trackerInfo = index.data (TrackerModel::TrackerRole).value(); 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 (""); const QString err_markup_end = QLatin1String (""); const QString timeout_markup_begin = QLatin1String (""); const QString timeout_markup_end = QLatin1String (""); const QString success_markup_begin = QLatin1String (""); const QString success_markup_end = QLatin1String (""); // hostname str += inf.st.isBackup ? QLatin1String ("") : QLatin1String (""); char * host = NULL; int port = 0; tr_urlParse (inf.st.announce.toUtf8().constData(), TR_BAD_SIZE, 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 ("") : QLatin1String (""); // 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 ("
\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 ("
\n"); str += tr ("No updates scheduled"); break; case TR_TRACKER_WAITING: { const QString tstr (timeToStringRounded (inf.st.nextAnnounceTime - now)); str += QLatin1String ("
\n"); //: %1 is duration str += tr ("Asking for more peers in %1").arg (tstr); break; } case TR_TRACKER_QUEUED: str += QLatin1String ("
\n"); str += tr ("Queued to ask for more peers"); break; case TR_TRACKER_ACTIVE: { const QString tstr (timeToStringRounded (now - inf.st.lastAnnounceStartTime)); str += QLatin1String ("
\n"); //: %1 is duration str += tr ("Asking for more peers now... %1").arg (tstr); break; } } if (myShowMore) { if (inf.st.hasScraped) { str += QLatin1String ("
\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 ("
\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 ("
\n"); str += tr ("Queued to ask for peer counts"); break; } case TR_TRACKER_ACTIVE: { str += QLatin1String ("
\n"); const QString tstr (timeToStringRounded (now - inf.st.lastScrapeStartTime)); //: %1 is duration str += tr ("Asking for peer counts now... %1").arg (tstr); break; } } } } return str; }