source: trunk/qt/torrent.cc @ 10536

Last change on this file since 10536 was 10536, checked in by charles, 12 years ago

(trunk) #3174 "Use IEC standard units (KiB, MiB, GiB) instead of (KB, MB, GB)" -- implemented for gtk, qt, daemon, cli, and web client

  • Property svn:keywords set to Date Rev Author Id
File size: 20.6 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: torrent.cc 10536 2010-04-28 01:26:46Z charles $
11 */
12
13#include <cassert>
14#include <iostream>
15
16#include <QApplication>
17#include <QFileIconProvider>
18#include <QFileInfo>
19#include <QSet>
20#include <QString>
21#include <QStyle>
22#include <QVariant>
23
24#include <libtransmission/transmission.h>
25#include <libtransmission/bencode.h>
26#include <libtransmission/utils.h> /* tr_new0, tr_strdup */
27
28#include "app.h"
29#include "prefs.h"
30#include "torrent.h"
31#include "utils.h"
32
33
34Torrent :: Torrent( Prefs& prefs, int id ):
35    myPrefs( prefs )
36{
37    for( int i=0; i<PROPERTY_COUNT; ++i )
38        assert( myProperties[i].id == i );
39
40    setInt( ID, id );
41    setIcon( MIME_ICON, QApplication::style()->standardIcon( QStyle::SP_FileIcon ) );
42}
43
44Torrent :: ~Torrent( )
45{
46}
47
48/***
49****
50***/
51
52Torrent :: Property
53Torrent :: myProperties[] =
54{
55    { ID, "id", QVariant::Int, INFO, },
56    { UPLOAD_SPEED, "rateUpload", QVariant::Int, STAT } /* B/s */,
57    { DOWNLOAD_SPEED, "rateDownload", QVariant::Int, STAT }, /* B/s */
58    { DOWNLOAD_DIR, "downloadDir", QVariant::String, STAT },
59    { ACTIVITY, "status", QVariant::Int, STAT },
60    { NAME, "name", QVariant::String, INFO },
61    { ERROR, "error", QVariant::Int, STAT },
62    { ERROR_STRING, "errorString", QVariant::String, STAT },
63    { SIZE_WHEN_DONE, "sizeWhenDone", QVariant::ULongLong, STAT },
64    { LEFT_UNTIL_DONE, "leftUntilDone", QVariant::ULongLong, STAT },
65    { HAVE_UNCHECKED, "haveUnchecked", QVariant::ULongLong, STAT },
66    { HAVE_VERIFIED, "haveValid", QVariant::ULongLong, STAT },
67    { DESIRED_AVAILABLE, "desiredAvailable", QVariant::ULongLong, STAT },
68    { TOTAL_SIZE, "totalSize", QVariant::ULongLong, INFO },
69    { PIECE_SIZE, "pieceSize", QVariant::ULongLong, INFO },
70    { PIECE_COUNT, "pieceCount", QVariant::Int, INFO },
71    { PEERS_GETTING_FROM_US, "peersGettingFromUs", QVariant::Int, STAT },
72    { PEERS_SENDING_TO_US, "peersSendingToUs", QVariant::Int, STAT },
73    { WEBSEEDS_SENDING_TO_US, "webseedsSendingToUs", QVariant::Int, STAT_EXTRA },
74    { PERCENT_DONE, "percentDone", QVariant::Double, STAT },
75    { METADATA_PERCENT_DONE, "metadataPercentComplete", QVariant::Double, STAT },
76    { PERCENT_VERIFIED, "recheckProgress", QVariant::Double, STAT },
77    { DATE_ACTIVITY, "activityDate", QVariant::DateTime, STAT_EXTRA },
78    { DATE_ADDED, "addedDate", QVariant::DateTime, INFO },
79    { DATE_STARTED, "startDate", QVariant::DateTime, STAT_EXTRA },
80    { DATE_CREATED, "dateCreated", QVariant::DateTime, INFO },
81    { PEERS_CONNECTED, "peersConnected", QVariant::Int, STAT },
82    { ETA, "eta", QVariant::Int, STAT },
83    { RATIO, "uploadRatio", QVariant::Double, STAT },
84    { DOWNLOADED_EVER, "downloadedEver", QVariant::ULongLong, STAT },
85    { UPLOADED_EVER, "uploadedEver", QVariant::ULongLong, STAT },
86    { FAILED_EVER, "corruptEver", QVariant::ULongLong, STAT_EXTRA },
87    { TRACKERS, "trackers", QVariant::StringList, INFO },
88    { TRACKERSTATS, "trackerStats", TrTypes::TrackerStatsList, STAT_EXTRA },
89    { MIME_ICON, "ccc", QVariant::Icon, DERIVED },
90    { SEED_RATIO_LIMIT, "seedRatioLimit", QVariant::Double, STAT_EXTRA },
91    { SEED_RATIO_MODE, "seedRatioMode", QVariant::Int, STAT_EXTRA },
92    { DOWN_LIMIT, "downloadLimit", QVariant::Int, STAT_EXTRA }, /* KiB/s */
93    { DOWN_LIMITED, "downloadLimited", QVariant::Bool, STAT_EXTRA },
94    { UP_LIMIT, "uploadLimit", QVariant::Int, STAT_EXTRA }, /* KiB/s */
95    { UP_LIMITED, "uploadLimited", QVariant::Bool, STAT_EXTRA },
96    { HONORS_SESSION_LIMITS, "honorsSessionLimits", QVariant::Bool, STAT_EXTRA },
97    { PEER_LIMIT, "peer-limit", QVariant::Int, STAT_EXTRA },
98    { HASH_STRING, "hashString", QVariant::String, INFO },
99    { IS_FINISHED, "isFinished", QVariant::Bool, STAT },
100    { IS_PRIVATE, "isPrivate", QVariant::Bool, INFO },
101    { COMMENT, "comment", QVariant::String, INFO },
102    { CREATOR, "creator", QVariant::String, INFO },
103    { MANUAL_ANNOUNCE_TIME, "manualAnnounceTime", QVariant::DateTime, STAT_EXTRA },
104    { PEERS, "peers", TrTypes::PeerList, STAT_EXTRA },
105    { TORRENT_FILE, "torrentFile", QVariant::String, STAT_EXTRA },
106    { BANDWIDTH_PRIORITY, "bandwidthPriority", QVariant::Int, STAT_EXTRA }
107};
108
109Torrent :: KeyList
110Torrent :: buildKeyList( Group group )
111{
112    KeyList keys;
113
114    if( keys.empty( ) )
115        for( int i=0; i<PROPERTY_COUNT; ++i )
116            if( myProperties[i].id==ID || myProperties[i].group==group )
117                keys << myProperties[i].key;
118
119    return keys;
120}
121
122const Torrent :: KeyList&
123Torrent :: getInfoKeys( )
124{
125    static KeyList keys;
126    if( keys.isEmpty( ) )
127        keys << buildKeyList( INFO ) << "files";
128    return keys;
129}
130
131const Torrent :: KeyList&
132Torrent :: getStatKeys( )
133{
134    static KeyList keys( buildKeyList( STAT ) );
135    return keys;
136}
137
138const Torrent :: KeyList&
139Torrent :: getExtraStatKeys( )
140{
141    static KeyList keys;
142    if( keys.isEmpty( ) )
143        keys << buildKeyList( STAT_EXTRA ) << "fileStats";
144    return keys;
145}
146
147
148bool
149Torrent :: setInt( int i, int value )
150{
151    bool changed = false;
152
153    assert( 0<=i && i<PROPERTY_COUNT );
154    assert( myProperties[i].type == QVariant::Int );
155
156    if( myValues[i].isNull() || myValues[i].toInt()!=value )
157    {
158        myValues[i].setValue( value );
159        changed = true;
160    }
161
162    return changed;
163}
164
165bool
166Torrent :: setBool( int i, bool value )
167{
168    bool changed = false;
169
170    assert( 0<=i && i<PROPERTY_COUNT );
171    assert( myProperties[i].type == QVariant::Bool );
172
173    if( myValues[i].isNull() || myValues[i].toBool()!=value )
174    {
175        myValues[i].setValue( value );
176        changed = true;
177    }
178
179    return changed;
180}
181
182bool
183Torrent :: setDouble( int i, double value )
184{
185    bool changed = false;
186
187    assert( 0<=i && i<PROPERTY_COUNT );
188    assert( myProperties[i].type == QVariant::Double );
189
190    if( myValues[i].isNull() || myValues[i].toDouble()!=value )
191    {
192        myValues[i].setValue( value );
193        changed = true;
194    }
195
196    return changed;
197}
198
199bool
200Torrent :: setDateTime( int i, const QDateTime& value )
201{
202    bool changed = false;
203
204    assert( 0<=i && i<PROPERTY_COUNT );
205    assert( myProperties[i].type == QVariant::DateTime );
206
207    if( myValues[i].isNull() || myValues[i].toDateTime()!=value )
208    {
209        myValues[i].setValue( value );
210        changed = true;
211    }
212
213    return changed;
214}
215
216bool
217Torrent :: setSize( int i, qulonglong value )
218{
219    bool changed = false;
220
221    assert( 0<=i && i<PROPERTY_COUNT );
222    assert( myProperties[i].type == QVariant::ULongLong );
223
224    if( myValues[i].isNull() || myValues[i].toULongLong()!=value )
225    {
226        myValues[i].setValue( value );
227        changed = true;
228    }
229
230    return changed;
231}
232
233bool
234Torrent :: setString( int i, const char * value )
235{
236    bool changed = false;
237
238    assert( 0<=i && i<PROPERTY_COUNT );
239    assert( myProperties[i].type == QVariant::String );
240
241    if( myValues[i].isNull() || myValues[i].toString()!=value )
242    {
243        myValues[i].setValue( QString::fromUtf8( value ) );
244        changed = true;
245    }
246
247    return changed;
248}
249
250bool
251Torrent :: setIcon( int i, const QIcon& value )
252{
253    assert( 0<=i && i<PROPERTY_COUNT );
254    assert( myProperties[i].type == QVariant::Icon );
255
256    myValues[i].setValue( value );
257    return true;
258}
259
260int
261Torrent :: getInt( int i ) const
262{
263    assert( 0<=i && i<PROPERTY_COUNT );
264    assert( myProperties[i].type == QVariant::Int );
265
266    return myValues[i].toInt( );
267}
268
269QDateTime
270Torrent :: getDateTime( int i ) const
271{
272    assert( 0<=i && i<PROPERTY_COUNT );
273    assert( myProperties[i].type == QVariant::DateTime );
274
275    return myValues[i].toDateTime( );
276}
277
278bool
279Torrent :: getBool( int i ) const
280{
281    assert( 0<=i && i<PROPERTY_COUNT );
282    assert( myProperties[i].type == QVariant::Bool );
283
284    return myValues[i].toBool( );
285}
286
287qulonglong
288Torrent :: getSize( int i ) const
289{
290    assert( 0<=i && i<PROPERTY_COUNT );
291    assert( myProperties[i].type == QVariant::ULongLong );
292
293    return myValues[i].toULongLong( );
294}
295double
296Torrent :: getDouble( int i ) const
297{
298    assert( 0<=i && i<PROPERTY_COUNT );
299    assert( myProperties[i].type == QVariant::Double );
300
301    return myValues[i].toDouble( );
302}
303QString
304Torrent :: getString( int i ) const
305{
306    assert( 0<=i && i<PROPERTY_COUNT );
307    assert( myProperties[i].type == QVariant::String );
308
309    return myValues[i].toString( );
310}
311QIcon
312Torrent :: getIcon( int i ) const
313{
314    assert( 0<=i && i<PROPERTY_COUNT );
315    assert( myProperties[i].type == QVariant::Icon );
316
317    return myValues[i].value<QIcon>();
318}
319
320/***
321****
322***/
323
324bool
325Torrent :: getSeedRatio( double& ratio ) const
326{
327    bool isLimited;
328
329    switch( seedRatioMode( ) )
330    {
331        case TR_RATIOLIMIT_SINGLE:
332            isLimited = true;
333            ratio = seedRatioLimit( );
334            break;
335
336        case TR_RATIOLIMIT_GLOBAL:
337            if(( isLimited = myPrefs.getBool( Prefs :: RATIO_ENABLED )))
338                ratio = myPrefs.getDouble( Prefs :: RATIO );
339            break;
340
341        case TR_RATIOLIMIT_UNLIMITED:
342            isLimited = false;
343            break;
344    }
345
346    return isLimited;
347}
348
349bool
350Torrent :: hasFileSubstring( const QString& substr ) const
351{
352    foreach( const TrFile file, myFiles )
353        if( file.filename.contains( substr, Qt::CaseInsensitive ) )
354            return true;
355    return false;
356}
357
358bool
359Torrent :: hasTrackerSubstring( const QString& substr ) const
360{
361    foreach( QString s, myValues[TRACKERS].toStringList() )
362        if( s.contains( substr, Qt::CaseInsensitive ) )
363            return true;
364    return false;
365}
366
367int
368Torrent :: compareRatio( const Torrent& that ) const
369{
370    const double a = ratio( );
371    const double b = that.ratio( );
372    if( (int)a == TR_RATIO_INF && (int)b == TR_RATIO_INF ) return 0;
373    if( (int)a == TR_RATIO_INF ) return 1;
374    if( (int)b == TR_RATIO_INF ) return -1;
375    if( a < b ) return -1;
376    if( a > b ) return 1;
377    return 0;
378}
379
380int
381Torrent :: compareETA( const Torrent& that ) const
382{
383    const bool haveA( hasETA( ) );
384    const bool haveB( that.hasETA( ) );
385    if( haveA && haveB ) return getETA() - that.getETA();
386    if( haveA ) return 1;
387    if( haveB ) return -1;
388    return 0;
389}
390
391int
392Torrent :: compareTracker( const Torrent& that ) const
393{
394    Q_UNUSED( that );
395
396    // FIXME
397    return 0;
398}
399
400/***
401****
402***/
403
404void
405Torrent :: updateMimeIcon( )
406{
407    const FileList& files( myFiles );
408
409    QIcon icon;
410
411    if( files.size( ) > 1 )
412        icon = QFileIconProvider().icon( QFileIconProvider::Folder );
413    else if( files.size( ) == 1 )
414        icon = Utils :: guessMimeIcon( files.at(0).filename );
415    else
416        icon = QIcon( );
417
418    setIcon( MIME_ICON, icon );
419}
420
421/***
422****
423***/
424
425void
426Torrent :: notifyComplete( ) const
427{
428    // if someone wants to implement notification, here's the hook.
429}
430
431/***
432****
433***/
434
435void
436Torrent :: update( tr_benc * d )
437{
438    bool changed = false;
439
440    for( int  i=0; i<PROPERTY_COUNT; ++i )
441    {
442        tr_benc * child = tr_bencDictFind( d, myProperties[i].key );
443        if( !child )
444            continue;
445
446        switch( myProperties[i].type )
447        {
448            case QVariant :: Int: {
449                int64_t val;
450                if( tr_bencGetInt( child, &val ) )
451                    changed |= setInt( i, val );
452                break;
453            }
454
455            case QVariant :: Bool: {
456                tr_bool val;
457                if( tr_bencGetBool( child, &val ) )
458                    changed |= setBool( i, val );
459                break;
460            }
461
462            case QVariant :: String: {
463                const char * val;
464                if( tr_bencGetStr( child, &val ) )
465                    changed |= setString( i, val );
466                break;
467            }
468
469            case QVariant :: ULongLong: {
470                int64_t val;
471                if( tr_bencGetInt( child, &val ) )
472                    changed |= setSize( i, val );
473                break;
474            }
475
476            case QVariant :: Double: {
477                double val;
478                if( tr_bencGetReal( child, &val ) )
479                    changed |= setDouble( i, val );
480                break;
481            }
482
483            case QVariant :: DateTime: {
484                int64_t val;
485                if( tr_bencGetInt( child, &val ) && val )
486                    changed |= setDateTime( i, QDateTime :: fromTime_t( val ) );
487                break;
488            }
489
490            case QVariant :: StringList:
491            case TrTypes :: PeerList:
492                break;
493
494            default:
495                assert( 0 && "unhandled type" );
496        }
497    }
498
499    tr_benc * files;
500
501    if( tr_bencDictFindList( d, "files", &files ) ) {
502        const char * str;
503        int64_t intVal;
504        int i = 0;
505        myFiles.clear( );
506        tr_benc * child;
507        while(( child = tr_bencListChild( files, i ))) {
508            TrFile file;
509            file.index = i++;
510            if( tr_bencDictFindStr( child, "name", &str ) )
511                file.filename = QString::fromUtf8( str );
512            if( tr_bencDictFindInt( child, "length", &intVal ) )
513                file.size = intVal;
514            myFiles.append( file );
515        }
516        updateMimeIcon( );
517        changed = true;
518    }
519
520    if( tr_bencDictFindList( d, "fileStats", &files ) ) {
521        const int n = tr_bencListSize( files );
522        assert( n == myFiles.size( ) );
523        for( int i=0; i<n; ++i ) {
524            int64_t intVal;
525            tr_bool boolVal;
526            tr_benc * child = tr_bencListChild( files, i );
527            TrFile& file( myFiles[i] );
528            if( tr_bencDictFindInt( child, "bytesCompleted", &intVal ) )
529                file.have = intVal;
530            if( tr_bencDictFindBool( child, "wanted", &boolVal ) )
531                file.wanted = boolVal;
532            if( tr_bencDictFindInt( child, "priority", &intVal ) )
533                file.priority = intVal;
534        }
535        changed = true;
536    }
537
538    tr_benc * trackers;
539    if( tr_bencDictFindList( d, "trackers", &trackers ) ) {
540        const char * str;
541        int i = 0;
542        QStringList list;
543        tr_benc * child;
544        while(( child = tr_bencListChild( trackers, i++ )))
545            if( tr_bencDictFindStr( child, "announce", &str ))
546                list.append( QString::fromUtf8( str ) );
547        if( myValues[TRACKERS] != list ) {
548            myValues[TRACKERS].setValue( list );
549            changed = true;
550        }
551    }
552
553    tr_benc * trackerStats;
554    if( tr_bencDictFindList( d, "trackerStats", &trackerStats ) ) {
555        tr_benc * child;
556        TrackerStatsList  trackerStatsList;
557        int childNum = 0;
558        while(( child = tr_bencListChild( trackerStats, childNum++ ))) {
559            tr_bool b;
560            int64_t i;
561            const char * str;
562            TrackerStat trackerStat;
563            if( tr_bencDictFindStr( child, "announce", &str ) )
564                trackerStat.announce = QString::fromUtf8( str );
565            if( tr_bencDictFindInt( child, "announceState", &i ) )
566                trackerStat.announceState = i;
567            if( tr_bencDictFindInt( child, "downloadCount", &i ) )
568                trackerStat.downloadCount = i;
569            if( tr_bencDictFindBool( child, "hasAnnounced", &b ) )
570                trackerStat.hasAnnounced = b;
571            if( tr_bencDictFindBool( child, "hasScraped", &b ) )
572                trackerStat.hasScraped = b;
573            if( tr_bencDictFindStr( child, "host", &str ) )
574                trackerStat.host = QString::fromUtf8( str );
575            if( tr_bencDictFindInt( child, "id", &i ) )
576                trackerStat.id = i;
577            if( tr_bencDictFindBool( child, "isBackup", &b ) )
578                trackerStat.isBackup = b;
579            if( tr_bencDictFindInt( child, "lastAnnouncePeerCount", &i ) )
580                trackerStat.lastAnnouncePeerCount = i;
581            if( tr_bencDictFindInt( child, "lastAnnounceResult", &i ) )
582                trackerStat.lastAnnounceResult = i;
583            if( tr_bencDictFindInt( child, "lastAnnounceStartTime", &i ) )
584                trackerStat.lastAnnounceStartTime = i;
585            if( tr_bencDictFindBool( child, "lastAnnounceSucceeded", &b ) )
586                trackerStat.lastAnnounceSucceeded = b;
587            if( tr_bencDictFindInt( child, "lastAnnounceTime", &i ) )
588                trackerStat.lastAnnounceTime = i;
589            if( tr_bencDictFindBool( child, "lastAnnounceTimedOut", &b ) )
590                trackerStat.lastAnnounceTimedOut = b;
591            if( tr_bencDictFindStr( child, "lastScrapeResult", &str ) )
592                trackerStat.lastScrapeResult = QString::fromUtf8( str );
593            if( tr_bencDictFindInt( child, "lastScrapeStartTime", &i ) )
594                trackerStat.lastScrapeStartTime = i;
595            if( tr_bencDictFindBool( child, "lastScrapeSucceeded", &b ) )
596                trackerStat.lastScrapeSucceeded = b;
597            if( tr_bencDictFindInt( child, "lastScrapeTime", &i ) )
598                trackerStat.lastScrapeTime = i;
599            if( tr_bencDictFindBool( child, "lastScrapeTimedOut", &b ) )
600                trackerStat.lastScrapeTimedOut = b;
601            if( tr_bencDictFindInt( child, "leecherCount", &i ) )
602                trackerStat.leecherCount = i;
603            if( tr_bencDictFindInt( child, "nextAnnounceTime", &i ) )
604                trackerStat.nextAnnounceTime = i;
605            if( tr_bencDictFindInt( child, "nextScrapeTime", &i ) )
606                trackerStat.nextScrapeTime = i;
607            if( tr_bencDictFindInt( child, "scrapeState", &i ) )
608                trackerStat.scrapeState = i;
609            if( tr_bencDictFindInt( child, "seederCount", &i ) )
610                trackerStat.seederCount = i;
611            if( tr_bencDictFindInt( child, "tier", &i ) )
612                trackerStat.tier = i;
613            trackerStatsList << trackerStat;
614        }
615        myValues[TRACKERSTATS].setValue( trackerStatsList );
616        changed = true;
617    }
618
619    tr_benc * peers;
620    if( tr_bencDictFindList( d, "peers", &peers ) ) {
621        tr_benc * child;
622        PeerList peerList;
623        int childNum = 0;
624        while(( child = tr_bencListChild( peers, childNum++ ))) {
625            double d;
626            tr_bool b;
627            int64_t i;
628            const char * str;
629            Peer peer;
630            if( tr_bencDictFindStr( child, "address", &str ) )
631                peer.address = QString::fromUtf8( str );
632            if( tr_bencDictFindStr( child, "clientName", &str ) )
633                peer.clientName = QString::fromUtf8( str );
634            if( tr_bencDictFindBool( child, "clientIsChoked", &b ) )
635                peer.clientIsChoked = b;
636            if( tr_bencDictFindBool( child, "clientIsInterested", &b ) )
637                peer.clientIsInterested = b;
638            if( tr_bencDictFindBool( child, "isDownloadingFrom", &b ) )
639                peer.isDownloadingFrom = b;
640            if( tr_bencDictFindBool( child, "isEncrypted", &b ) )
641                peer.isEncrypted = b;
642            if( tr_bencDictFindBool( child, "isIncoming", &b ) )
643                peer.isIncoming = b;
644            if( tr_bencDictFindBool( child, "isUploadingTo", &b ) )
645                peer.isUploadingTo = b;
646            if( tr_bencDictFindBool( child, "peerIsChoked", &b ) )
647                peer.peerIsChoked = b;
648            if( tr_bencDictFindBool( child, "peerIsInterested", &b ) )
649                peer.peerIsInterested = b;
650            if( tr_bencDictFindInt( child, "port", &i ) )
651                peer.port = i;
652            if( tr_bencDictFindReal( child, "progress", &d ) )
653                peer.progress = d;
654            if( tr_bencDictFindInt( child, "rateToClient", &i ) )
655                peer.rateToClient = Speed::fromBps( i );
656            if( tr_bencDictFindInt( child, "rateToPeer", &i ) )
657                peer.rateToPeer = Speed::fromBps( i );
658            peerList << peer;
659        }
660        myValues[PEERS].setValue( peerList );
661        changed = true;
662    }
663
664    if( changed )
665        emit torrentChanged( id( ) );
666}
667
668QString
669Torrent :: activityString( ) const
670{
671    QString str;
672
673    switch( getActivity( ) )
674    {
675        case TR_STATUS_CHECK_WAIT: str = tr( "Waiting to verify local data" ); break;
676        case TR_STATUS_CHECK:      str = tr( "Verifying local data" ); break;
677        case TR_STATUS_DOWNLOAD:   str = tr( "Downloading" ); break;
678        case TR_STATUS_SEED:       str = tr( "Seeding" ); break;
679        case TR_STATUS_STOPPED:    str = isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
680    }
681
682    return str;
683}
684
685QString
686Torrent :: getError( ) const
687{
688    QString s = getString( ERROR_STRING );
689
690    switch( getInt( ERROR ) )
691    {
692        case TR_STAT_TRACKER_WARNING: s = tr( "Tracker gave a warning: %1" ).arg( s ); break;
693        case TR_STAT_TRACKER_ERROR: s = tr( "Tracker gave an error: %1" ).arg( s ); break;
694        case TR_STAT_LOCAL_ERROR: s = tr( "Error: %1" ).arg( s ); break;
695        default: s.clear(); break;
696    }
697
698    return s;
699}
Note: See TracBrowser for help on using the repository browser.