/* * This file Copyright (C) 2008 Charles Kerr * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id$ */ #include /* qsort */ #include /* memcpy */ #include "transmission.h" #include "rpc-utils.h" #include "torrent.h" #include "utils.h" /**** ***** ****/ static int compareTorrentsByActivity( const void * a, const void * b ) { const tr_stat * sa = tr_torrentStatCached( *(tr_torrent**) a ); const tr_stat * sb = tr_torrentStatCached( *(tr_torrent**) b ); int i; if(( i = tr_compareDouble( sa->rateUpload + sa->rateDownload, sb->rateUpload + sb->rateDownload ) )) return i; if( sa->uploadedEver != sb->uploadedEver ) return sa->uploadedEver < sa->uploadedEver ? -1 : 1; return 0; } static int compareTorrentsByAge( const void * a, const void * b ) { const tr_torrent * ta = * (tr_torrent **) a; const tr_torrent * tb = * (tr_torrent **) b; return tr_compareTime( tr_torrentStatCached( ta )->addedDate, tr_torrentStatCached( tb )->addedDate ); } static int compareTorrentsByID( const void * a, const void * b ) { const tr_torrent * ta = * (tr_torrent **) a; const tr_torrent * tb = * (tr_torrent **) b; return ta->uniqueId - tb->uniqueId; } static int compareTorrentsByName( const void * a, const void * b ) { const tr_torrent * ta = * (tr_torrent **) a; const tr_torrent * tb = * (tr_torrent **) b; return tr_strcasecmp( ta->info.name, tb->info.name ); } static int compareRatio( double a, double b ) { if( (int)a == TR_RATIO_INF && (int)b == TR_RATIO_INF ) return 0; if( (int)a == TR_RATIO_INF ) return 1; if( (int)b == TR_RATIO_INF ) return -1; return tr_compareDouble( a, b ); } static int compareTorrentsByProgress( const void * a, const void * b ) { const tr_stat * sa = tr_torrentStatCached( *(tr_torrent**) a ); const tr_stat * sb = tr_torrentStatCached( *(tr_torrent**) b ); int ret = tr_compareDouble( sa->percentDone, sb->percentDone ); if( !ret ) ret = compareRatio( sa->ratio, sb->ratio ); return ret; } static int compareTorrentsByRatio( const void * a, const void * b ) { const tr_stat * sa = tr_torrentStatCached( *(tr_torrent**) a ); const tr_stat * sb = tr_torrentStatCached( *(tr_torrent**) b ); return compareRatio( sa->ratio, sb->ratio ); } static int compareTorrentsByState( const void * a, const void * b ) { const tr_stat * sa = tr_torrentStatCached( *(tr_torrent**) a ); const tr_stat * sb = tr_torrentStatCached( *(tr_torrent**) b ); int ret = sa->status - sb->status; if( !ret ) ret = compareTorrentsByRatio( a, b ); return 0; } static int compareTorrentsByTracker( const void * a, const void * b ) { const tr_stat * sa = tr_torrentStatCached( *(tr_torrent**) a ); const tr_stat * sb = tr_torrentStatCached( *(tr_torrent**) b ); return tr_strcmp( sa->announceURL, sb->announceURL ); } typedef int( *compareFunc )( const void *, const void * ); void tr_torrentSort( tr_torrent ** torrents, int torrentCount, tr_sort_method sortMethod, int isAscending ) { compareFunc func = NULL; switch( sortMethod ) { case TR_SORT_ACTIVITY: func = &compareTorrentsByActivity; break; case TR_SORT_AGE: func = compareTorrentsByAge; break; case TR_SORT_NAME: func = compareTorrentsByName; break; case TR_SORT_PROGRESS: func = compareTorrentsByProgress; break; case TR_SORT_RATIO: func = compareTorrentsByRatio; break; case TR_SORT_STATE: func = compareTorrentsByState; break; case TR_SORT_TRACKER: func = compareTorrentsByTracker; break; default: func = compareTorrentsByID; break; } qsort( torrents, torrentCount, sizeof(tr_torrent*), func ); if( !isAscending ) { int left = 0; int right = torrentCount - 1; while( left < right ) { tr_torrent * tmp = torrents[left]; torrents[left] = torrents[right]; torrents[right] = tmp; ++left; --right; } } } /**** ***** ****/ static int testActive( const tr_torrent * tor ) { const tr_stat * s = tr_torrentStatCached( ( tr_torrent * ) tor ); return s->peersSendingToUs>0 || s->peersGettingFromUs>0; } static int testStatus( const tr_torrent * tor, cp_status_t status ) { return tr_torrentGetStatus( ( tr_torrent * ) tor ) == status; } static int testDownloading( const tr_torrent * tor ) { return testStatus( tor, TR_STATUS_DOWNLOAD ); } static int testSeeding( const tr_torrent * tor ) { return testStatus( tor, TR_STATUS_SEED ); } static int testPaused( const tr_torrent * tor ) { return testStatus( tor, TR_STATUS_STOPPED ); } static int testTrue( const tr_torrent * tor UNUSED ) { return TRUE; } typedef int( *test_func )( const tr_torrent * ); void tr_torrentFilter( tr_torrent ** torrents, int * torrentCount, tr_filter_method filterMethod ) { int i; int newCount = 0; test_func func; tr_torrent ** tmp = tr_new0( tr_torrent*, torrentCount ); switch( filterMethod ) { case TR_FILTER_ACTIVE: func = testActive; break; case TR_FILTER_DOWNLOADING: func = testDownloading; break; case TR_FILTER_PAUSED: func = testPaused; break; case TR_FILTER_SEEDING: func = testSeeding; break; default: func = testTrue; break; } for( i=0; i<*torrentCount; ++i ) if( func( torrents[i] ) ) tmp[newCount++] = torrents[i]; memcpy( torrents, tmp, sizeof(tr_torrent*) * newCount ); *torrentCount = newCount; tr_free( tmp ); }