2 * This file Copyright (C) Mnemosyne LLC
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: torrent-delegate.cc 13244 2012-03-04 13:15:43Z jordan $
15 #include <QApplication>
17 #include <QFontMetrics>
19 #include <QModelIndex>
22 #include <QPixmapCache>
23 #include <QStyleOptionProgressBarV2>
25 #include "formatter.h"
27 #include "torrent-delegate.h"
28 #include "torrent-model.h"
36 QColor
TorrentDelegate :: greenBrush
;
37 QColor
TorrentDelegate :: blueBrush
;
38 QColor
TorrentDelegate :: greenBack
;
39 QColor
TorrentDelegate :: blueBack
;
41 TorrentDelegate :: TorrentDelegate( QObject
* parent
):
42 QStyledItemDelegate( parent
),
43 myProgressBarStyle( new QStyleOptionProgressBarV2
)
45 myProgressBarStyle
->minimum
= 0;
46 myProgressBarStyle
->maximum
= 1000;
48 greenBrush
= QColor("forestgreen");
49 greenBack
= QColor("darkseagreen");
51 blueBrush
= QColor("steelblue");
52 blueBack
= QColor("lightgrey");
55 TorrentDelegate :: ~TorrentDelegate( )
57 delete myProgressBarStyle
;
65 TorrentDelegate :: margin( const QStyle
& style
) const
73 TorrentDelegate :: progressString( const Torrent
& tor
) const
75 const bool isMagnet( !tor
.hasMetadata( ) );
76 const bool isDone( tor
.isDone( ) );
77 const bool isSeed( tor
.isSeed( ) );
78 const uint64_t haveTotal( tor
.haveTotal( ) );
81 const bool hasSeedRatio( tor
.getSeedRatio( seedRatio
) );
83 if( isMagnet
) // magnet link with no metadata
85 /* %1 is the percentage of torrent metadata downloaded */
86 str
= tr( "Magnetized transfer - retrieving metadata (%1%)" )
87 .arg( Formatter::percentToString( tor
.metadataPercentDone() * 100.0 ) );
89 else if( !isDone
) // downloading
91 /* %1 is how much we've got,
92 %2 is how much we'll have when done,
93 %3 is a percentage of the two */
94 str
= tr( "%1 of %2 (%3%)" ).arg( Formatter::sizeToString( haveTotal
) )
95 .arg( Formatter::sizeToString( tor
.sizeWhenDone( ) ) )
96 .arg( Formatter::percentToString( tor
.percentDone( ) * 100.0 ) );
98 else if( !isSeed
) // partial seed
102 /* %1 is how much we've got,
103 %2 is the torrent's total size,
104 %3 is a percentage of the two,
105 %4 is how much we've uploaded,
106 %5 is our upload-to-download ratio
107 %6 is the ratio we want to reach before we stop uploading */
108 str
= tr( "%1 of %2 (%3%), uploaded %4 (Ratio: %5 Goal: %6)" )
109 .arg( Formatter::sizeToString( haveTotal
) )
110 .arg( Formatter::sizeToString( tor
.totalSize( ) ) )
111 .arg( Formatter::percentToString( tor
.percentComplete( ) * 100.0 ) )
112 .arg( Formatter::sizeToString( tor
.uploadedEver( ) ) )
113 .arg( Formatter::ratioToString( tor
.ratio( ) ) )
114 .arg( Formatter::ratioToString( seedRatio
) );
118 /* %1 is how much we've got,
119 %2 is the torrent's total size,
120 %3 is a percentage of the two,
121 %4 is how much we've uploaded,
122 %5 is our upload-to-download ratio */
123 str
= tr( "%1 of %2 (%3%), uploaded %4 (Ratio: %5)" )
124 .arg( Formatter::sizeToString( haveTotal
) )
125 .arg( Formatter::sizeToString( tor
.totalSize( ) ) )
126 .arg( Formatter::percentToString( tor
.percentComplete( ) * 100.0 ) )
127 .arg( Formatter::sizeToString( tor
.uploadedEver( ) ) )
128 .arg( Formatter::ratioToString( tor
.ratio( ) ) );
135 /* %1 is the torrent's total size,
136 %2 is how much we've uploaded,
137 %3 is our upload-to-download ratio,
138 %4 is the ratio we want to reach before we stop uploading */
139 str
= tr( "%1, uploaded %2 (Ratio: %3 Goal: %4)" )
140 .arg( Formatter::sizeToString( haveTotal
) )
141 .arg( Formatter::sizeToString( tor
.uploadedEver( ) ) )
142 .arg( Formatter::ratioToString( tor
.ratio( ) ) )
143 .arg( Formatter::ratioToString( seedRatio
) );
145 else /* seeding w/o a ratio */
147 /* %1 is the torrent's total size,
148 %2 is how much we've uploaded,
149 %3 is our upload-to-download ratio */
150 str
= tr( "%1, uploaded %2 (Ratio: %3)" )
151 .arg( Formatter::sizeToString( haveTotal
) )
152 .arg( Formatter::sizeToString( tor
.uploadedEver( ) ) )
153 .arg( Formatter::ratioToString( tor
.ratio( ) ) );
157 /* add time when downloading */
158 if( ( hasSeedRatio
&& tor
.isSeeding( ) ) || tor
.isDownloading( ) )
162 str
+= tr( "%1 left" ).arg( Formatter::timeToString( tor
.getETA( ) ) );
164 str
+= tr( "Remaining time unknown" );
171 TorrentDelegate :: shortTransferString( const Torrent
& tor
) const
173 static const QChar
upArrow( 0x2191 );
174 static const QChar
downArrow( 0x2193 );
175 const bool haveMeta( tor
.hasMetadata( ) );
176 const bool haveDown( haveMeta
&& tor
.peersWeAreDownloadingFrom( ) > 0 );
177 const bool haveUp( haveMeta
&& tor
.peersWeAreUploadingTo( ) > 0 );
178 QString downStr
, upStr
, str
;
181 downStr
= Formatter::speedToString( tor
.downloadSpeed( ) );
183 upStr
= Formatter::speedToString( tor
.uploadSpeed( ) );
185 if( haveDown
&& haveUp
)
186 str
= tr( "%1 %2, %3 %4" ).arg(downArrow
).arg(downStr
).arg(upArrow
).arg(upStr
);
188 str
= tr( "%1 %2" ).arg(downArrow
).arg(downStr
);
190 str
= tr( "%1 %2" ).arg(upArrow
).arg(upStr
);
191 else if( tor
.isStalled( ) )
192 str
= tr( "Stalled" );
193 else if( tor
.hasMetadata( ) )
200 TorrentDelegate :: shortStatusString( const Torrent
& tor
) const
204 switch( tor
.getActivity( ) )
206 case TR_STATUS_CHECK
:
207 str
= tr( "Verifying local data (%1% tested)" ).arg( Formatter::percentToString( tor
.getVerifyProgress()*100.0 ) );
210 case TR_STATUS_DOWNLOAD
:
212 if( !tor
.isDownloading( ) )
213 str
= tr( "Ratio: %1, " ).arg( Formatter::ratioToString( tor
.ratio( ) ) );
214 str
+= shortTransferString( tor
);
218 str
= tor
.activityString( );
226 TorrentDelegate :: statusString( const Torrent
& tor
) const
230 if( tor
.hasError( ) )
232 str
= tor
.getError( );
234 else switch( tor
.getActivity( ) )
236 case TR_STATUS_STOPPED
:
237 case TR_STATUS_CHECK_WAIT
:
238 case TR_STATUS_CHECK
:
239 case TR_STATUS_DOWNLOAD_WAIT
:
240 case TR_STATUS_SEED_WAIT
:
241 str
= shortStatusString( tor
);
244 case TR_STATUS_DOWNLOAD
:
245 if( tor
.hasMetadata( ) )
246 str
= tr( "Downloading from %1 of %n connected peer(s)", 0, tor
.connectedPeersAndWebseeds( ) )
247 .arg( tor
.peersWeAreDownloadingFrom( ) );
249 str
= tr( "Downloading metadata from %n peer(s) (%1% done)", 0, tor
.peersWeAreDownloadingFrom( ) )
250 .arg( Formatter::percentToString( 100.0 * tor
.metadataPercentDone( ) ) );
254 str
= tr( "Seeding to %1 of %n connected peer(s)", 0, tor
.connectedPeers( ) )
255 .arg( tor
.peersWeAreUploadingTo( ) );
263 if( tor
.isReadyToTransfer( ) ) {
264 QString s
= shortTransferString( tor
);
266 str
+= tr( " - " ) + s
;
278 int MAX3( int a
, int b
, int c
)
280 const int ab( a
> b
? a
: b
);
281 return ab
> c
? ab
: c
;
286 TorrentDelegate :: sizeHint( const QStyleOptionViewItem
& option
, const Torrent
& tor
) const
288 const QStyle
* style( QApplication::style( ) );
289 static const int iconSize( style
->pixelMetric( QStyle::PM_MessageBoxIconSize
) );
291 QFont
nameFont( option
.font
);
292 nameFont
.setWeight( QFont::Bold
);
293 const QFontMetrics
nameFM( nameFont
);
294 const QString
nameStr( tor
.name( ) );
295 const int nameWidth
= nameFM
.width( nameStr
);
296 QFont
statusFont( option
.font
);
297 statusFont
.setPointSize( int( option
.font
.pointSize( ) * 0.9 ) );
298 const QFontMetrics
statusFM( statusFont
);
299 const QString
statusStr( statusString( tor
) );
300 const int statusWidth
= statusFM
.width( statusStr
);
301 QFont
progressFont( statusFont
);
302 const QFontMetrics
progressFM( progressFont
);
303 const QString
progressStr( progressString( tor
) );
304 const int progressWidth
= progressFM
.width( progressStr
);
305 const QSize
m( margin( *style
) );
306 return QSize( m
.width()*2 + iconSize
+ GUI_PAD
+ MAX3( nameWidth
, statusWidth
, progressWidth
),
307 //m.height()*3 + nameFM.lineSpacing() + statusFM.lineSpacing()*2 + progressFM.lineSpacing() );
308 m
.height()*3 + nameFM
.lineSpacing() + statusFM
.lineSpacing() + BAR_HEIGHT
+ progressFM
.lineSpacing() );
312 TorrentDelegate :: sizeHint( const QStyleOptionViewItem
& option
,
313 const QModelIndex
& index
) const
315 const Torrent
* tor( index
.data( TorrentModel::TorrentRole
).value
<const Torrent
*>() );
316 return sizeHint( option
, *tor
);
320 TorrentDelegate :: paint( QPainter
* painter
,
321 const QStyleOptionViewItem
& option
,
322 const QModelIndex
& index
) const
324 const Torrent
* tor( index
.data( TorrentModel::TorrentRole
).value
<const Torrent
*>() );
326 painter
->setClipRect( option
.rect
);
327 drawTorrent( painter
, option
, *tor
);
332 TorrentDelegate :: setProgressBarPercentDone( const QStyleOptionViewItem
& option
, const Torrent
& tor
) const
334 double seedRatioLimit
;
335 if (tor
.isSeeding() && tor
.getSeedRatio(seedRatioLimit
))
337 const double seedRateRatio
= tor
.ratio() / seedRatioLimit
;
338 const int scaledProgress
= seedRateRatio
* (myProgressBarStyle
->maximum
- myProgressBarStyle
->minimum
);
339 myProgressBarStyle
->progress
= myProgressBarStyle
->minimum
+ scaledProgress
;
343 const bool isMagnet( !tor
.hasMetadata( ) );
344 myProgressBarStyle
->direction
= option
.direction
;
345 myProgressBarStyle
->progress
= int(myProgressBarStyle
->minimum
+ (((isMagnet
? tor
.metadataPercentDone() : tor
.percentDone()) * (myProgressBarStyle
->maximum
- myProgressBarStyle
->minimum
))));
350 TorrentDelegate :: drawTorrent( QPainter
* painter
, const QStyleOptionViewItem
& option
, const Torrent
& tor
) const
352 const QStyle
* style( QApplication::style( ) );
353 static const int iconSize( style
->pixelMetric( QStyle::PM_LargeIconSize
) );
354 QFont
nameFont( option
.font
);
355 nameFont
.setWeight( QFont::Bold
);
356 const QFontMetrics
nameFM( nameFont
);
357 const QString
nameStr( tor
.name( ) );
358 const QSize
nameSize( nameFM
.size( 0, nameStr
) );
359 QFont
statusFont( option
.font
);
360 statusFont
.setPointSize( int( option
.font
.pointSize( ) * 0.9 ) );
361 const QFontMetrics
statusFM( statusFont
);
362 const QString
statusStr( progressString( tor
) );
363 QFont
progressFont( statusFont
);
364 const QFontMetrics
progressFM( progressFont
);
365 const QString
progressStr( statusString( tor
) );
366 const bool isPaused( tor
.isPaused( ) );
370 if (option
.state
& QStyle::State_Selected
) {
371 QPalette::ColorGroup cg
= option
.state
& QStyle::State_Enabled
372 ? QPalette::Normal
: QPalette::Disabled
;
373 if (cg
== QPalette::Normal
&& !(option
.state
& QStyle::State_Active
))
374 cg
= QPalette::Inactive
;
376 painter
->fillRect(option
.rect
, option
.palette
.brush(cg
, QPalette::Highlight
));
380 if( isPaused
|| !(option
.state
& QStyle::State_Enabled
) ) im
= QIcon::Disabled
;
381 else if( option
.state
& QStyle::State_Selected
) im
= QIcon::Selected
;
382 else im
= QIcon::Normal
;
385 if( isPaused
) qs
= QIcon::Off
;
388 QPalette::ColorGroup cg
= QPalette::Normal
;
389 if( isPaused
|| !(option
.state
& QStyle::State_Enabled
) ) cg
= QPalette::Disabled
;
390 if( cg
== QPalette::Normal
&& !(option
.state
& QStyle::State_Active
) ) cg
= QPalette::Inactive
;
392 QPalette::ColorRole cr
;
393 if( option
.state
& QStyle::State_Selected
) cr
= QPalette::HighlightedText
;
394 else cr
= QPalette::Text
;
396 QStyle::State
progressBarState( option
.state
);
397 if( isPaused
) progressBarState
= QStyle::State_None
;
398 progressBarState
|= QStyle::State_Small
;
401 const QSize
m( margin( *style
) );
402 QRect
fillArea( option
.rect
);
403 fillArea
.adjust( m
.width(), m
.height(), -m
.width(), -m
.height() );
404 QRect
iconArea( fillArea
.x( ), fillArea
.y( ) + ( fillArea
.height( ) - iconSize
) / 2, iconSize
, iconSize
);
405 QRect
nameArea( iconArea
.x( ) + iconArea
.width( ) + GUI_PAD
, fillArea
.y( ),
406 fillArea
.width( ) - GUI_PAD
- iconArea
.width( ), nameSize
.height( ) );
407 QRect
statusArea( nameArea
);
408 statusArea
.moveTop( nameArea
.y( ) + nameFM
.lineSpacing( ) );
409 statusArea
.setHeight( nameSize
.height( ) );
410 QRect
barArea( statusArea
);
411 barArea
.setHeight( BAR_HEIGHT
);
412 barArea
.moveTop( statusArea
.y( ) + statusFM
.lineSpacing( ) );
413 QRect
progArea( statusArea
);
414 progArea
.moveTop( barArea
.y( ) + barArea
.height( ) );
417 if( tor
.hasError( ) )
418 painter
->setPen( QColor( "red" ) );
420 painter
->setPen( option
.palette
.color( cg
, cr
) );
421 tor
.getMimeTypeIcon().paint( painter
, iconArea
, Qt::AlignCenter
, im
, qs
);
422 painter
->setFont( nameFont
);
423 painter
->drawText( nameArea
, 0, nameFM
.elidedText( nameStr
, Qt::ElideRight
, nameArea
.width( ) ) );
424 painter
->setFont( statusFont
);
425 painter
->drawText( statusArea
, 0, statusFM
.elidedText( statusStr
, Qt::ElideRight
, statusArea
.width( ) ) );
426 painter
->setFont( progressFont
);
427 painter
->drawText( progArea
, 0, progressFM
.elidedText( progressStr
, Qt::ElideRight
, progArea
.width( ) ) );
428 myProgressBarStyle
->rect
= barArea
;
429 if ( tor
.isDownloading() ) {
430 myProgressBarStyle
->palette
.setBrush( QPalette::Highlight
, blueBrush
);
431 myProgressBarStyle
->palette
.setColor( QPalette::Base
, blueBack
);
432 myProgressBarStyle
->palette
.setColor( QPalette::Background
, blueBack
);
434 else if ( tor
.isSeeding() ) {
435 myProgressBarStyle
->palette
.setBrush( QPalette::Highlight
, greenBrush
);
436 myProgressBarStyle
->palette
.setColor( QPalette::Base
, greenBack
);
437 myProgressBarStyle
->palette
.setColor( QPalette::Background
, greenBack
);
439 myProgressBarStyle
->state
= progressBarState
;
440 setProgressBarPercentDone( option
, tor
);
442 style
->drawControl( QStyle::CE_ProgressBar
, myProgressBarStyle
, painter
);