source: trunk/wx/torrent-list.cc @ 2469

Last change on this file since 2469 was 2469, checked in by charles, 15 years ago

torrent filter counts work now.

File size: 15.7 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
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
11#include <iostream>
12#include <wx/intl.h>
13#include <torrent-list.h>
14
15namespace
16{
17    typedef std::vector<tr_torrent_t*> torrents_t;
18
19    enum
20    {
21        COL_POSITION,
22        COL_DONE,
23        COL_DOWNLOAD_SPEED,
24        COL_ETA,
25        COL_HASH,
26        COL_NAME,
27        COL_PEERS,
28        COL_RATIO,
29        COL_RECEIVED,
30        COL_REMAINING,
31        COL_SEEDS,
32        COL_SENT,
33        COL_SIZE,
34        COL_STATE,
35        COL_STATUS,
36        COL_TOTAL,
37        COL_UPLOAD_SPEED,
38        N_COLS
39    };
40
41    const wxString columnKeys[N_COLS] =
42    {
43        _T("position"),
44        _T("done"),
45        _T("download-speed"),
46        _T("eta"),
47        _T("hash"),
48        _T("name"),
49        _T("peers"),
50        _T("ratio"),
51        _T("received"),
52        _T("remaining"),
53        _T("seeds"),
54        _T("sent"),
55        _T("size"),
56        _T("state"),
57        _T("status"),
58        _T("total"),
59        _T("upload-speed")
60    };
61
62    int getTorrentColumn( const wxString& key )
63    {
64        typedef std::map<wxString,int> string2key_t;
65        static string2key_t columns;
66
67        if( columns.empty() )
68        {
69            columns[_T("position")]        = COL_POSITION;
70            columns[_T("done")]            = COL_DONE;
71            columns[_T("download-speed")]  = COL_DOWNLOAD_SPEED;
72            columns[_T("eta")]             = COL_ETA;
73            columns[_T("hash")]            = COL_HASH;
74            columns[_T("name")]            = COL_NAME;
75            columns[_T("peers")]           = COL_PEERS;
76            columns[_T("ratio")]           = COL_RATIO;
77            columns[_T("received")]        = COL_RECEIVED;
78            columns[_T("remaining")]       = COL_REMAINING;
79            columns[_T("seeds")]           = COL_SEEDS; 
80            columns[_T("sent")]            = COL_SENT; 
81            columns[_T("size")]            = COL_SIZE; 
82            columns[_T("state")]           = COL_STATE; 
83            columns[_T("status")]          = COL_STATUS; 
84            columns[_T("total")]           = COL_TOTAL; 
85            columns[_T("upload-speed")]    = COL_UPLOAD_SPEED;
86        }
87
88        int i = -1;
89        string2key_t::const_iterator it = columns.find( key );
90        if( it != columns.end() )
91            i = it->second;
92
93        return i;
94    }
95
96    typedef std::vector<int> int_v;
97
98    int_v getTorrentColumns( wxConfig * config )
99    {
100        const wxString key = _T("torrent-list-columns");
101        wxString columnStr;
102        if( !config->Read( key, &columnStr, _T("name|download-speed|upload-speed|eta|peers|size|done|status|seeds") ) )
103            config->Write( key, columnStr );
104
105        int_v cols;
106        while( !columnStr.IsEmpty() )
107        {
108            const wxString key = columnStr.BeforeFirst(_T('|'));
109            columnStr.Remove( 0, key.Len() + 1 );
110            cols.push_back( getTorrentColumn( key ) );
111        }
112        return cols;
113    }
114
115    int bestDecimal( double num ) {
116        if ( num < 10 ) return 2;
117        if ( num < 100 ) return 1;
118        return 0;
119    }
120
121    wxString toWxStr( const std::string& s )
122    {
123        return wxString( s.c_str(), wxConvUTF8 );
124    }
125
126    wxString toWxStr( const char * s )
127    {
128        return wxString( s, wxConvUTF8 );
129    }
130
131    wxString getReadableSize( uint64_t size )
132    {
133        int i;
134        static const char *sizestrs[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
135        for ( i=0; size>>10; ++i ) size = size>>10;
136        char buf[512];
137        snprintf( buf, sizeof(buf), "%.*f %s", bestDecimal(size), (double)size, sizestrs[i] );
138        return toWxStr( buf );
139    }
140
141    wxString getReadableSize( float f )
142    {
143        return getReadableSize( (uint64_t)f );
144    }
145
146    wxString getReadableSpeed( float f )
147    {
148        wxString xstr = getReadableSize(f);
149        xstr += _T("/s");
150        return xstr;
151    }
152
153    wxString getReadableTime( int i /*seconds*/ )  /*FIXME*/
154    {
155        const int s = i % 60; i /= 60;
156        const int m = i % 60; i /= 60;
157        const int h = i;
158        return wxString::Format( _T("%d:%02d:%02d"), h, m, s );
159    }
160}
161
162enum
163{
164    TORRENT_LIST_CTRL = 1000
165};
166
167BEGIN_EVENT_TABLE(TorrentListCtrl, wxListCtrl)
168    EVT_LIST_COL_CLICK( TORRENT_LIST_CTRL, TorrentListCtrl::OnSort )
169    EVT_LIST_ITEM_SELECTED( TORRENT_LIST_CTRL, TorrentListCtrl::OnItemSelected )
170    EVT_LIST_ITEM_DESELECTED( TORRENT_LIST_CTRL, TorrentListCtrl::OnItemDeselected )
171END_EVENT_TABLE()
172
173TorrentListCtrl :: TorrentListCtrl( tr_handle_t       * handle,
174                                    wxConfig          * config,
175                                    wxWindow          * parent,
176                                    const wxPoint     & pos,
177                                    const wxSize      & size):
178    wxListCtrl( parent, TORRENT_LIST_CTRL, pos, size, wxLC_REPORT ),
179    myHandle( handle ),
180    myConfig( config )
181{
182    wxString sortColStr;
183    myConfig->Read( _T("torrent-sort-column"), &sortColStr, columnKeys[COL_NAME] );
184    prevSortCol = getTorrentColumn( sortColStr );
185    bool descending;
186    myConfig->Read( _T("torrent-sort-is-descending"), &descending, FALSE );
187    if( descending )
188        prevSortCol = -prevSortCol;
189    Rebuild ();
190}
191
192TorrentListCtrl :: ~TorrentListCtrl()
193{
194}
195
196void
197TorrentListCtrl :: RefreshTorrent( tr_torrent_t  * tor,
198                                   int             myTorrents_index,
199                                   const int_v   & cols )
200{
201    int row = -1;
202    int col = 0;
203    char buf[512];
204    std::string str;
205    const tr_stat_t * s = tr_torrentStat( tor );
206    const tr_info_t* info = tr_torrentInfo( tor );
207
208    for( int_v::const_iterator it(cols.begin()), end(cols.end()); it!=end; ++it )
209    {
210        wxString xstr;
211
212        switch( *it )
213        {
214            case COL_POSITION:
215                snprintf( buf, sizeof(buf), "%d", 666 );
216                xstr = toWxStr( buf );
217                break;
218
219            case COL_DONE:
220                snprintf( buf, sizeof(buf), "%d%%", (int)(s->percentDone*100.0) );
221                xstr = toWxStr( buf );
222                break;
223
224            case COL_DOWNLOAD_SPEED: break;
225                xstr = getReadableSpeed( s->rateDownload );
226                break;
227
228            case COL_ETA:
229                if( (int)(s->percentDone*100) >= 100 )
230                    xstr = wxString ();
231                else if( s->eta < 0 )
232                    xstr = toWxStr( "\xE2\x88\x9E" ); /* infinity, in utf-8 */
233                else
234                    xstr = getReadableTime( s->eta );
235                break;
236               
237            case COL_HASH:
238                xstr = toWxStr( info->hashString );
239                break;
240
241            case COL_NAME:
242                xstr = toWxStr( info->name );
243                break;
244
245            case COL_PEERS:
246                /* FIXME: this is all peers, not just leechers */
247                snprintf( buf, sizeof(buf), "%d (%d)", s->peersTotal, s->peersConnected );
248                xstr = toWxStr( buf );
249                break;
250
251            case COL_RATIO:
252                snprintf( buf, sizeof(buf), "%%%d", (int)(s->uploaded / (double)s->downloadedValid) );
253                xstr = toWxStr( buf );
254                break;
255
256            case COL_RECEIVED:
257                xstr = getReadableSize( s->downloaded );
258                break;
259
260            case COL_REMAINING:
261                xstr = getReadableSize( s->left );
262                break;
263
264            case COL_SEEDS:
265                snprintf( buf, sizeof(buf), "%d", s->seeders ); /* FIXME: %d (%d) */
266                xstr = toWxStr( buf );
267                break;
268
269            case COL_SENT:
270                xstr = getReadableSize( s->uploaded );
271                break;
272
273            case COL_SIZE:
274                xstr = getReadableSize( info->totalSize );
275                break;
276
277            case COL_STATE:
278                xstr = _T("Fixme");
279                break;
280
281            case COL_STATUS:
282                xstr = _T("Fixme");
283                break;
284
285            case COL_TOTAL:
286                xstr = _T("Fixme");
287                break;
288
289            case COL_UPLOAD_SPEED:
290                xstr = getReadableSpeed( s->rateUpload );
291                break;
292
293            default:
294                xstr = _T("Fixme");
295        }
296
297        if( col )
298            SetItem( row, col++, xstr );
299        else {
300            // first column... find the right row to put the info in.
301            // if the torrent's in the list already, update that row.
302            // otherwise, add a new row.
303            if( row < 0 ) {
304                str2int_t::const_iterator it = myHashToRow.find( info->hashString );
305                if( it != myHashToRow.end() ) {
306                    row = it->second;
307                }
308            }
309            if( row >= 0 ) {
310                SetItem( row, col++, xstr );
311            }
312            else {
313                row = InsertItem( GetItemCount(), xstr );
314                col = 1;
315                myHashToRow[info->hashString] = row;
316                SetItemData( row, myTorrents_index );
317            }
318        }
319    }
320}
321
322/***
323****
324***/
325
326void
327TorrentListCtrl :: OnSort( wxListEvent& event )
328{
329    const int_v  cols = getTorrentColumns( myConfig );
330    const int key = cols[ event.GetColumn() ];
331    Sort( key );
332}
333
334void
335TorrentListCtrl :: OnItemSelected( wxListEvent& event )
336{
337    std::set<tr_torrent_t*> sel;
338    long item = -1;
339    for ( ;; ) {
340        item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
341        if ( item == -1 )
342            break;
343        sel.insert( myTorrents[GetItemData(item)] );
344    }
345    fire_selection_changed( sel );
346}
347
348void
349TorrentListCtrl :: OnItemDeselected( wxListEvent& event )
350{
351    OnItemSelected( event );
352}
353
354/***
355****
356***/
357
358static torrents_t * uglyHack = NULL;
359
360int
361TorrentListCtrl :: Compare( long item1, long item2, long sortData )
362{
363    const tr_torrent_t * a = (*uglyHack)[item1];
364    const tr_torrent_t * b = (*uglyHack)[item2];
365    const tr_info_t* ia = tr_torrentInfo( a );
366    const tr_info_t* ib = tr_torrentInfo( b );
367    int ret = 0;
368
369    switch( abs(sortData) )
370    {
371        case COL_POSITION:
372            ret = item1 - item2;
373
374        case COL_DONE:
375/*            ccc
376            snprintf( buf, sizeof(buf), "%d%%", (int)(s->percentDone*100.0) );
377            xstr = toWxStr( buf );*/
378            break;
379
380        case COL_DOWNLOAD_SPEED: break;
381            /*xstr = getReadableSpeed( s->rateDownload );*/
382            break;
383
384        case COL_ETA:
385/*            if( (int)(s->percentDone*100) >= 100 ) */
386            break;
387           
388        case COL_HASH:
389            /*xstr = toWxStr( info->hashString );*/
390            break;
391
392        case COL_NAME:
393            ret = strcmp( ia->name, ib->name );
394            break;
395
396        case COL_PEERS:
397            /* FIXME: this is all peers, not just leechers
398            snprintf( buf, sizeof(buf), "%d (%d)", s->peersTotal, s->peersConnected );
399            xstr = toWxStr( buf );*/
400            break;
401
402        case COL_RATIO:
403            /*snprintf( buf, sizeof(buf), "%%%d", (int)(s->uploaded / (double)s->downloadedValid) );
404            xstr = toWxStr( buf );*/
405            break;
406
407        case COL_RECEIVED:
408            /*xstr = getReadableSize( s->downloaded );*/
409            break;
410
411        case COL_REMAINING:
412            /*xstr = getReadableSize( s->left );*/
413            break;
414
415        case COL_SEEDS:
416            /*snprintf( buf, sizeof(buf), "%d", s->seeders );
417            xstr = toWxStr( buf );*/
418            break;
419
420        case COL_SENT:
421            /*xstr = getReadableSize( s->uploaded );*/
422            break;
423
424        case COL_SIZE:
425            if( ia->totalSize < ib->totalSize ) ret = -1;
426            else if( ia->totalSize > ib->totalSize ) ret = 1;
427            else ret = 0;
428            break;
429
430        case COL_STATE:
431            /*xstr = _T("Fixme");*/
432            break;
433
434        case COL_STATUS:
435            /*xstr = _T("Fixme");*/
436            break;
437
438        case COL_TOTAL:
439            /*xstr = _T("Fixme");*/
440            break;
441
442        case COL_UPLOAD_SPEED:
443            /*xstr = getReadableSpeed( s->rateUpload );*/
444            break;
445
446        default:
447            abort ();
448    }
449
450    if( sortData < 0 )
451       ret = -ret;
452
453    return ret;
454}
455
456void
457TorrentListCtrl :: Sort( int column )
458{
459    if( column == prevSortCol )
460        column = -column;
461    prevSortCol = column;
462    Resort ();
463}
464
465void
466TorrentListCtrl :: Resort( )
467{
468    uglyHack = &myTorrents;
469
470    myConfig->Write( _T("torrent-sort-column"), columnKeys[abs(prevSortCol)] );
471    myConfig->Write( _T("torrent-sort-is-descending"), prevSortCol < 0 );
472
473    SortItems( Compare, prevSortCol );
474
475    const int n = GetItemCount ();
476    str2int_t tmp;
477    for( int i=0; i<n; ++i ) {
478        int idx = GetItemData( i );
479        const tr_info_t* info = tr_torrentInfo( myTorrents[idx] );
480        tmp[info->hashString] = i;
481    }
482    myHashToRow.swap( tmp );
483    uglyHack = NULL;
484}
485
486/***
487****
488***/
489
490void
491TorrentListCtrl :: Refresh ()
492{
493    const int_v  cols = getTorrentColumns( myConfig );
494    const int rowCount = GetItemCount();
495    for( int row=0; row<rowCount; ++row )
496    {
497        int array_index = GetItemData( row );
498        tr_torrent_t * tor = myTorrents[array_index];
499        RefreshTorrent( tor, array_index, cols );
500    }
501}
502
503void
504TorrentListCtrl :: Repopulate ()
505{
506    DeleteAllItems();
507    myHashToRow.clear ();
508
509    const int_v cols = getTorrentColumns( myConfig );
510    int i = 0;
511    for( torrents_t::const_iterator it(myTorrents.begin()),
512                                   end(myTorrents.end()); it!=end; ++it )
513        RefreshTorrent( *it, i++, cols );
514
515    Resort( );
516}
517
518void
519TorrentListCtrl :: Rebuild()
520{
521    ClearAll( );
522    myHashToRow.clear ();
523
524    int i = 0;
525    const int_v  cols = getTorrentColumns( myConfig );
526    for( int_v ::const_iterator it(cols.begin()), end(cols.end()); it!=end; ++it )
527    {
528        int format = wxLIST_FORMAT_LEFT;
529        int width = -1;
530        wxString h;
531
532        switch( *it )
533        {
534            case COL_POSITION:        h = _("#"); format = wxLIST_FORMAT_CENTRE; break;
535            case COL_DONE:            h = _("Done"); format = wxLIST_FORMAT_RIGHT; break;
536            case COL_DOWNLOAD_SPEED:  h = _("Download"); break;
537            case COL_ETA:             h = _("ETA"); format = wxLIST_FORMAT_RIGHT; break;
538            case COL_HASH:            h = _("SHA1 Hash"); break;
539            case COL_NAME:            h = _("Name"); width = 500; break;
540            case COL_PEERS:           h = _("Peers"); format = wxLIST_FORMAT_RIGHT; break;
541            case COL_RATIO:           h = _("Ratio"); format = wxLIST_FORMAT_RIGHT; break;
542            case COL_RECEIVED:        h = _("Received"); format = wxLIST_FORMAT_RIGHT; break;
543            case COL_REMAINING:       h = _("Remaining"); format = wxLIST_FORMAT_RIGHT; break;
544            case COL_SEEDS:           h = _("Seeds"); format = wxLIST_FORMAT_RIGHT; break;
545            case COL_SENT:            h = _("Sent"); format = wxLIST_FORMAT_RIGHT; break;
546            case COL_SIZE:            h = _("Size");  format = wxLIST_FORMAT_RIGHT; break;
547            case COL_STATE:           h = _("State"); break;
548            case COL_STATUS:          h = _("Status"); break;
549            case COL_TOTAL:           h = _("Total"); break;
550            case COL_UPLOAD_SPEED:    h = _("Upload"); format = wxLIST_FORMAT_RIGHT;break;
551            default:                  h = _("Error"); break;
552        }
553
554        InsertColumn( i++, h, format, width );
555    }
556
557    Repopulate ();
558}
559
560void
561TorrentListCtrl :: Add( const torrents_t& add )
562{
563    myTorrents.insert( myTorrents.end(), add.begin(), add.end() );
564    Repopulate ();
565}
566
567
Note: See TracBrowser for help on using the repository browser.