source: trunk/wx/xmission.cc @ 2907

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

implement the `speed' tab.

  • Property svn:executable set to *
  • Property svn:keywords set to Date Rev Author Id
File size: 18.6 KB
Line 
1/*
2 * Xmission - a cross-platform bittorrent client
3 * Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 *
18 * $Id: xmission.cc 2907 2007-08-22 19:49:24Z charles $
19 */
20
21#include <inttypes.h>
22#include <set>
23#include <map>
24#include <string>
25#include <vector>
26#include <iostream>
27
28#include <wx/artprov.h>
29#include <wx/bitmap.h>
30#include <wx/cmdline.h>
31#include <wx/config.h>
32#include <wx/dcmemory.h>
33#include <wx/defs.h>
34#include <wx/filename.h>
35#include <wx/image.h>
36#include <wx/intl.h>
37#include <wx/listctrl.h>
38#include <wx/notebook.h>
39#include <wx/snglinst.h>
40#include <wx/splitter.h>
41#include <wx/statline.h>
42#include <wx/taskbar.h>
43#include <wx/tglbtn.h>
44#include <wx/toolbar.h>
45#include <wx/wx.h>
46#if wxCHECK_VERSION(2,8,0)
47#include <wx/aboutdlg.h>
48#endif
49
50extern "C"
51{
52  #include <libtransmission/transmission.h>
53  #include <libtransmission/utils.h>
54
55  #include <images/play.xpm>
56  #include <images/stop.xpm>
57  #include <images/plus.xpm>
58  #include <images/minus.xpm>
59
60  #include <images/systray.xpm>
61  #include <images/transmission.xpm>
62}
63
64#include "foreach.h"
65#include "speed-stats.h"
66#include "torrent-filter.h"
67#include "torrent-list.h"
68#include "torrent-stats.h"
69
70/***
71****
72***/
73
74namespace
75{
76    int bestDecimal( double num ) {
77        if ( num < 10 ) return 2;
78        if ( num < 100 ) return 1;
79        return 0;
80    }
81
82    wxString toWxStr( const char * s )
83    {
84        return wxString( s, wxConvUTF8 );
85    }
86
87    std::string toStr( const wxString& xstr )
88    {
89        return std::string( xstr.mb_str( *wxConvCurrent ) );
90    }
91
92    wxString getReadableSize( uint64_t size )
93    {
94        int i;
95        static const char *sizestrs[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
96        for ( i=0; size>>10; ++i ) size = size>>10;
97        char buf[512];
98        snprintf( buf, sizeof(buf), "%.*f %s", bestDecimal(size), (double)size, sizestrs[i] );
99        return toWxStr( buf );
100    }
101
102    wxString getReadableSize( float f )
103    {
104        return getReadableSize( (uint64_t)f );
105    }
106
107    wxString getReadableSpeed( float kib_sec )
108    {
109        wxString xstr = getReadableSize(1024*kib_sec);
110        xstr += _T("/s");
111        return xstr;
112    }
113}
114
115namespace
116{
117    const wxCmdLineEntryDesc cmdLineDesc[] =
118    {
119        { wxCMD_LINE_SWITCH, _T("p"), _("pause"), _("pauses all the torrents on startup"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
120        { wxCMD_LINE_NONE, NULL, NULL, NULL, wxCMD_LINE_VAL_STRING, 0 }
121    };
122}
123
124/***
125****
126***/
127
128class MyApp : public wxApp
129{
130    virtual bool OnInit();
131    virtual ~MyApp();
132    wxSingleInstanceChecker * myChecker;
133};
134
135namespace
136{
137    tr_handle_t * handle = NULL;
138
139    typedef std::vector<tr_torrent_t*> torrents_v;
140}
141
142class MyFrame : public wxFrame, public TorrentListCtrl::Listener
143{
144public:
145    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, bool paused);
146    virtual ~MyFrame();
147
148public:
149    void OnExit( wxCommandEvent& );
150    void OnAbout( wxCommandEvent& );
151    void OnOpen( wxCommandEvent& );
152
153    void OnStart( wxCommandEvent& );
154    void OnStartUpdate( wxUpdateUIEvent& );
155
156    void OnStop( wxCommandEvent& );
157    void OnStopUpdate( wxUpdateUIEvent& );
158
159    void OnRemove( wxCommandEvent& );
160    void OnRemoveUpdate( wxUpdateUIEvent& );
161
162    void OnRecheck( wxCommandEvent& );
163    void OnRecheckUpdate( wxUpdateUIEvent& );
164
165    void OnSelectAll( wxCommandEvent& );
166    void OnSelectAllUpdate( wxUpdateUIEvent& );
167    void OnDeselectAll( wxCommandEvent& );
168    void OnDeselectAllUpdate( wxUpdateUIEvent& );
169
170    void OnFilterToggled( wxCommandEvent& );
171
172    void OnPulse( wxTimerEvent& );
173
174    virtual void OnTorrentListSelectionChanged( TorrentListCtrl*, const std::set<tr_torrent_t*>& );
175
176private:
177    void RefreshFilterCounts( );
178    void ApplyCurrentFilter( );
179
180protected:
181    wxConfig * myConfig;
182    wxTimer myPulseTimer;
183
184private:
185    TorrentListCtrl * myTorrentList;
186    TorrentStats * myTorrentStats;
187    wxListCtrl * myFilters;
188    wxTaskBarIcon * myTrayIcon;
189    wxIcon myLogoIcon;
190    wxIcon myTrayIconIcon;
191    SpeedStats * mySpeedStats;
192    torrents_v myTorrents;
193    torrents_v mySelectedTorrents;
194    int myFilterFlags;
195    std::string mySavePath;
196    time_t myExitTime;
197    wxToggleButton * myFilterToggles[TorrentFilter::N_FILTERS];
198
199private:
200    DECLARE_EVENT_TABLE()
201};
202
203enum
204{
205    ID_START,
206    ID_DESELECTALL,
207    ID_EDIT_PREFS,
208    ID_SHOW_DEBUG_WINDOW,
209    ID_Pulse,
210    ID_Filter
211};
212
213BEGIN_EVENT_TABLE(MyFrame, wxFrame)
214    EVT_COMMAND_RANGE( ID_Filter, ID_Filter+TorrentFilter::N_FILTERS, wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, MyFrame::OnFilterToggled )
215    EVT_MENU     ( wxID_ABOUT, MyFrame::OnAbout )
216    EVT_TIMER    ( ID_Pulse, MyFrame::OnPulse )
217    EVT_MENU     ( wxID_EXIT, MyFrame::OnExit )
218    EVT_MENU     ( wxID_OPEN, MyFrame::OnOpen )
219    EVT_MENU     ( ID_START, MyFrame::OnStart )
220    EVT_UPDATE_UI( ID_START, MyFrame::OnStartUpdate )
221    EVT_MENU     ( wxID_STOP, MyFrame::OnStop )
222    EVT_UPDATE_UI( wxID_STOP, MyFrame::OnStopUpdate )
223    EVT_MENU     ( wxID_REFRESH, MyFrame::OnRecheck )
224    EVT_UPDATE_UI( wxID_REFRESH, MyFrame::OnRecheckUpdate )
225    EVT_MENU     ( wxID_REMOVE, MyFrame::OnRemove )
226    EVT_UPDATE_UI( wxID_REMOVE, MyFrame::OnRemoveUpdate )
227    EVT_MENU     ( wxID_SELECTALL, MyFrame::OnSelectAll )
228    EVT_UPDATE_UI( wxID_SELECTALL, MyFrame::OnSelectAllUpdate )
229    EVT_MENU     ( ID_DESELECTALL, MyFrame::OnDeselectAll )
230    EVT_UPDATE_UI( ID_DESELECTALL, MyFrame::OnDeselectAllUpdate )
231END_EVENT_TABLE()
232
233IMPLEMENT_APP(MyApp)
234
235
236void
237MyFrame :: OnFilterToggled( wxCommandEvent& e )
238{
239    const int index = e.GetId() - ID_Filter;
240    int flags = 1<<index;
241    const bool active = myFilterToggles[index]->GetValue ( );
242    if( active )
243        myFilterFlags |= flags;
244    else
245        myFilterFlags &= ~flags;
246    ApplyCurrentFilter ( );
247}
248
249void
250MyFrame :: OnSelectAll( wxCommandEvent& )
251{
252    myTorrentList->SelectAll( );
253}
254
255void
256MyFrame :: OnSelectAllUpdate( wxUpdateUIEvent& event )
257{
258    event.Enable( mySelectedTorrents.size() < myTorrents.size() );
259}
260
261void
262MyFrame :: OnDeselectAll( wxCommandEvent& )
263{
264    myTorrentList->DeselectAll ( );
265}
266
267void
268MyFrame :: OnDeselectAllUpdate( wxUpdateUIEvent& event )
269{
270    event.Enable( !mySelectedTorrents.empty() );
271}
272
273/**
274**/
275
276void
277MyFrame :: OnStartUpdate( wxUpdateUIEvent& event )
278{
279    unsigned long l = 0;
280    foreach( torrents_v, mySelectedTorrents, it )
281        l |= tr_torrentStat(*it)->status;
282    event.Enable( (l & TR_STATUS_INACTIVE)!=0 );
283}
284void
285MyFrame :: OnStart( wxCommandEvent& WXUNUSED(unused) )
286{
287    foreach( torrents_v, mySelectedTorrents, it )
288        if( tr_torrentStat(*it)->status & TR_STATUS_INACTIVE )
289            tr_torrentStart( *it );
290}
291
292/**
293**/
294
295void
296MyFrame :: OnStopUpdate( wxUpdateUIEvent& event )
297{
298    unsigned long l = 0;
299    foreach( torrents_v, mySelectedTorrents, it )
300        l |= tr_torrentStat(*it)->status;
301    event.Enable( (l & TR_STATUS_ACTIVE)!=0 );
302}
303void
304MyFrame :: OnStop( wxCommandEvent& WXUNUSED(unused) )
305{
306    foreach( torrents_v, mySelectedTorrents, it )
307        if( tr_torrentStat(*it)->status & TR_STATUS_ACTIVE )
308            tr_torrentStop( *it );
309}
310
311/**
312**/
313
314void
315MyFrame :: OnRemoveUpdate( wxUpdateUIEvent& event )
316{
317    event.Enable( !mySelectedTorrents.empty() );
318}
319void
320MyFrame :: OnRemove( wxCommandEvent& WXUNUSED(unused) )
321{
322    foreach( torrents_v, mySelectedTorrents, it ) {
323        tr_torrentRemoveSaved( *it );
324        tr_torrentClose( *it );
325    }
326}
327
328/**
329**/
330
331void
332MyFrame :: OnRecheckUpdate( wxUpdateUIEvent& event )
333{
334   event.Enable( !mySelectedTorrents.empty() );
335}
336void
337MyFrame :: OnRecheck( wxCommandEvent& WXUNUSED(unused) )
338{
339    foreach( torrents_v, mySelectedTorrents, it )
340        tr_torrentRecheck( *it );
341}
342
343/**
344**/
345
346void MyFrame :: OnOpen( wxCommandEvent& WXUNUSED(event) )
347{
348    const wxString key = _T("prev-directory");
349    wxString directory;
350    myConfig->Read( key, &directory );
351    wxFileDialog * w = new wxFileDialog( this, _T("message"),
352                                         directory,
353                                         _T(""), /* default file */
354                                         _T("Torrent files|*.torrent"),
355                                         wxOPEN|wxMULTIPLE );
356
357    if( w->ShowModal() == wxID_OK )
358    {
359        wxArrayString paths;
360        w->GetPaths( paths );
361        size_t nPaths = paths.GetCount();
362        for( size_t i=0; i<nPaths; ++i )
363        {
364            const std::string filename = toStr( paths[i] );
365            tr_torrent_t * tor = tr_torrentInit( handle,
366                                                 filename.c_str(),
367                                                 mySavePath.c_str(),
368                                                 0, NULL );
369            if( tor )
370                myTorrents.push_back( tor );
371        }
372        ApplyCurrentFilter( );
373
374        myConfig->Write( key, w->GetDirectory() );
375    }
376
377    delete w;
378}
379
380
381bool MyApp::OnInit()
382{
383    handle = tr_init( "wx" );
384
385    wxCmdLineParser cmdParser( cmdLineDesc, argc, argv );
386    if( cmdParser.Parse ( ) )
387        return false;
388
389    const wxString name = wxString::Format( _T("Xmission-%s"), wxGetUserId().c_str());
390    myChecker = new wxSingleInstanceChecker( name );
391    if ( myChecker->IsAnotherRunning() ) {
392        wxLogError(_("An instance of Xmission is already running."));
393        return false;
394    }
395
396    const bool paused = cmdParser.Found( _("p") );
397
398    MyFrame * frame = new MyFrame( _("Xmission"),
399                                   wxPoint(50,50),
400                                   wxSize(900,600),
401                                   paused);
402
403    frame->Show( true );
404    SetTopWindow( frame );
405    return true;
406}
407
408MyApp :: ~MyApp()
409{
410    delete myChecker;
411}
412
413/***
414****
415***/
416
417void
418MyFrame :: RefreshFilterCounts( )
419{
420    int hits[ TorrentFilter :: N_FILTERS ];
421    TorrentFilter::CountHits( myTorrents, hits );
422    for( int i=0; i<TorrentFilter::N_FILTERS; ++i )
423        myFilterToggles[i]->SetLabel( TorrentFilter::GetName( i, hits[i] ) );
424}
425
426void
427MyFrame :: ApplyCurrentFilter( )
428{
429    torrents_v tmp( myTorrents );
430    TorrentFilter :: RemoveFailures( myFilterFlags, tmp );
431    myTorrentList->Assign( tmp );
432}
433
434void
435MyFrame :: OnTorrentListSelectionChanged( TorrentListCtrl* list,
436                                          const std::set<tr_torrent_t*>& torrents )
437{
438    assert( list == myTorrentList );
439    mySelectedTorrents.assign( torrents.begin(), torrents.end() );
440
441    tr_torrent_t * tor = mySelectedTorrents.empty() ? NULL : mySelectedTorrents.front();
442    mySpeedStats->SetTorrent( tor );
443}
444
445void
446MyFrame :: OnPulse(wxTimerEvent& WXUNUSED(event) )
447{
448    if( myExitTime ) {
449        if ( !tr_torrentCount(handle) ||  myExitTime<time(0) ) {
450            delete myTrayIcon;
451            myTrayIcon = 0;
452            Destroy( );
453            return;
454        }
455    }
456
457    RefreshFilterCounts( );
458    ApplyCurrentFilter( );
459
460    mySpeedStats->Pulse( handle );
461
462    float down, up;
463    tr_torrentRates( handle, &down, &up );
464    wxString xstr = _("Total DL: ");
465    xstr += getReadableSpeed( down );
466    SetStatusText( xstr, 1 );
467    xstr = _("Total UL: ");
468    xstr += getReadableSpeed( up );
469    SetStatusText( xstr, 2 );
470
471    xstr = _("Download: ");
472    xstr += getReadableSpeed( down );
473    xstr += _T("\n");
474    xstr +=_("Upload: ");
475    xstr +=  getReadableSpeed( up );
476    myTrayIcon->SetIcon( myTrayIconIcon, xstr );
477
478    myTorrentList->Refresh ( );
479}
480
481MyFrame::~MyFrame()
482{
483    myTorrentList->RemoveListener( this );
484    delete myTorrentList;
485
486    delete myConfig;
487}
488
489MyFrame :: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, bool paused):
490    wxFrame((wxFrame*)NULL,-1,title,pos,size),
491    myConfig( new wxConfig( _T("xmission") ) ),
492    myPulseTimer( this, ID_Pulse ),
493    myLogoIcon( transmission_xpm ),
494    myTrayIconIcon( systray_xpm ),
495    mySpeedStats( 0 ),
496    myFilterFlags( ~0 ),
497    myExitTime( 0 )
498{
499    myTrayIcon = new wxTaskBarIcon;
500    SetIcon( myLogoIcon );
501
502    long port;
503    wxString key = _T("port");
504    if( !myConfig->Read( key, &port, 9090 ) )
505        myConfig->Write( key, port );
506    tr_setBindPort( handle, port );
507
508    key = _T("save-path");
509    wxString wxstr;
510    if( !myConfig->Read( key, &wxstr, wxFileName::GetHomeDir() ) )
511        myConfig->Write( key, wxstr );
512    mySavePath = toStr( wxstr );
513    std::cerr << __FILE__ << ':' << __LINE__ << " save-path is [" << mySavePath << ']' << std::endl;
514
515    /**
516    ***  Menu
517    **/
518
519    wxMenuBar *menuBar = new wxMenuBar;
520
521    wxMenu * m = new wxMenu;
522    m->Append( wxID_OPEN, _T("&Open") );
523    m->Append( ID_START, _T("&Start") );
524    m->Append( wxID_STOP, _T("Sto&p") ) ;
525    m->Append( wxID_REFRESH, _T("Re&check") );
526    m->Append( wxID_REMOVE, _T("&Remove") );
527    m->AppendSeparator();
528    m->Append( wxID_NEW, _T("Create &New Torrent") );
529    m->AppendSeparator();
530    m->Append( wxID_CLOSE, _T("&Close") );
531    m->Append( wxID_EXIT, _T("&Exit") );
532    menuBar->Append( m, _T("&File") );
533
534    m = new wxMenu;
535    m->Append( wxID_SELECTALL, _T("Select &All") );
536    m->Append( ID_DESELECTALL, _T("&Deselect All") );
537    m->AppendSeparator();
538    m->Append( wxID_PREFERENCES, _T("Edit &Preferences") );
539    menuBar->Append( m, _T("&Edit") );
540
541    m = new wxMenu;
542    m->Append( ID_SHOW_DEBUG_WINDOW, _T("Show &Debug Window") );
543    m->AppendSeparator();
544    m->Append( wxID_ABOUT, _T("&About Xmission") );
545    menuBar->Append( m, _T("&Help") );
546
547    SetMenuBar(menuBar);
548
549    /**
550    ***  Toolbar
551    **/
552
553    wxIcon open_icon( plus_xpm );
554    wxIcon exec_icon( play_xpm );
555    wxIcon stop_icon( stop_xpm );
556    wxIcon drop_icon( minus_xpm );
557    wxBitmap bitmap;
558
559    wxToolBar* toolbar = CreateToolBar( wxTB_FLAT );
560    toolbar->SetToolBitmapSize( wxSize( 24, 24 ) );
561    bitmap.CopyFromIcon( open_icon );
562    toolbar->AddTool( wxID_OPEN,   _T("Open"), bitmap );
563    bitmap.CopyFromIcon( exec_icon );
564    toolbar->AddTool( ID_START,    _T("Start"), bitmap );
565    bitmap.CopyFromIcon( stop_icon );
566    toolbar->AddTool( wxID_STOP,   _T("Stop"), bitmap );
567    bitmap.CopyFromIcon( drop_icon );
568    toolbar->AddTool( wxID_REMOVE, _T("Remove"), bitmap );
569    toolbar->Realize();
570
571    /**
572    ***  Row 1
573    **/
574
575    wxSplitterWindow * hsplit = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH );
576#if wxCHECK_VERSION(2,5,4)
577    hsplit->SetSashGravity( 0.8 );
578#endif
579
580    wxPanel * panel_1 = new wxPanel( hsplit, wxID_ANY );
581
582    wxBoxSizer * buttonSizer = new wxBoxSizer( wxHORIZONTAL );
583
584    wxStaticText * text = new wxStaticText( panel_1, wxID_ANY, _("Show:") );
585    buttonSizer->Add( text, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 3 );
586
587    int rightButtonSpacing[TorrentFilter::N_FILTERS] = { 0, 0, 10, 0, 10, 0, 0 };
588    for( int i=0; i<TorrentFilter::N_FILTERS; ++i ) {
589        wxToggleButton * tb = new wxToggleButton( panel_1, ID_Filter+i, TorrentFilter::GetName(i) );
590        tb->SetValue( true );
591        myFilterToggles[i] = tb;
592        //buttonSizer->Add( tb, 0, wxRIGHT, rightButtonSpacing[i] );
593        buttonSizer->Add( tb, 1, wxEXPAND|wxRIGHT, rightButtonSpacing[i] );
594    }
595
596    myTorrentList = new TorrentListCtrl( handle, myConfig, panel_1 );
597    myTorrentList->AddListener( this );
598
599    wxBoxSizer * panelSizer = new wxBoxSizer( wxVERTICAL );
600    panelSizer->Add( new wxStaticLine( panel_1 ), 0, wxEXPAND, 0 );
601    panelSizer->Add( buttonSizer, 0, 0, 0 );
602    panelSizer->Add( myTorrentList, 1, wxEXPAND, 0 );
603
604    panel_1->SetSizer( panelSizer );
605
606
607    wxNotebook * notebook = new wxNotebook( hsplit, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP );
608    myTorrentStats = new TorrentStats( notebook );
609    notebook->AddPage( myTorrentStats, _T("General"), false );
610    wxButton * tmp = new wxButton( notebook, wxID_ANY, _T("Hello &World"));
611    notebook->AddPage( tmp, _T("Peers") );
612    tmp = new wxButton( notebook, wxID_ANY, _T("&Hello World"));
613    notebook->AddPage( tmp, _T("Pieces") );
614    tmp = new wxButton( notebook, wxID_ANY, _T("Hello World"));
615    notebook->AddPage( tmp, _T("Files") );
616    mySpeedStats = new SpeedStats( notebook, wxID_ANY );
617    notebook->AddPage( mySpeedStats, _T("Speed"), true );
618    tmp = new wxButton( notebook, wxID_ANY, _T("Hello World"));
619    notebook->AddPage( tmp, _T("Logger") );
620
621    hsplit->SplitHorizontally( panel_1, notebook );
622
623    /**
624    ***  Statusbar
625    **/
626
627    const int widths[] = { -1, 150, 150 };
628    wxStatusBar * statusBar = CreateStatusBar( WXSIZEOF(widths) );
629    SetStatusWidths( WXSIZEOF(widths), widths );
630    const int styles[] = { wxSB_FLAT, wxSB_NORMAL, wxSB_NORMAL };
631    statusBar->SetStatusStyles(  WXSIZEOF(widths), styles );
632
633    /**
634    ***  Refresh
635    **/
636
637    myPulseTimer.Start( 1500 );
638
639    /**
640    ***  Load the torrents
641    **/
642
643    int flags = 0;
644    if( paused )
645        flags |= TR_FLAG_PAUSED;
646    int count = 0;
647    tr_torrent_t ** torrents = tr_loadTorrents ( handle, mySavePath.c_str(), flags, &count );
648    myTorrents.insert( myTorrents.end(), torrents, torrents+count );
649    tr_free( torrents );
650
651    wxTimerEvent dummy;
652    OnPulse( dummy );
653}
654
655void MyFrame::OnExit(wxCommandEvent& WXUNUSED(event))
656{
657    Enable( false );
658
659    foreach( torrents_v, myTorrents, it )
660        tr_torrentClose( *it );
661
662    myTorrents.clear ();
663    mySelectedTorrents.clear ();
664
665    ApplyCurrentFilter ();
666
667    /* give the connections a max of 10 seconds to shut themselves down */
668    myExitTime = time(0) + 10;
669}
670
671void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
672{
673    wxIcon ico( transmission_xpm );
674
675#if wxCHECK_VERSION(2,8,0)
676    wxAboutDialogInfo info;
677    info.SetName(_T("Xmission"));
678    info.SetVersion(_T(LONG_VERSION_STRING));
679    info.SetCopyright(_T("Copyright 2005-2007 The Transmission Project"));
680    info.SetDescription(_T("A fast, lightweight bittorrent client"));
681    info.SetWebSite( _T( "http://transmission.m0k.org/" ) );
682    info.SetIcon( ico );
683    info.AddDeveloper( _T("Josh Elsasser (Back-end; GTK+)") );
684    info.AddDeveloper( _T("Charles Kerr (Back-end, GTK+, wxWidgets)") );
685    info.AddDeveloper( _T("Mitchell Livingston (Back-end; OS X)")  );
686    info.AddDeveloper( _T("Eric Petit (Back-end; OS X)")  );
687    info.AddDeveloper( _T("Bryan Varner (BeOS)")  );
688    wxAboutBox( info );
689#else
690    wxMessageBox(_T("Xmission " LONG_VERSION_STRING "\n"
691                    "Copyright 2005-2007 The Transmission Project"),
692                 _T("About Xmission"),
693                wxOK|wxICON_INFORMATION, this);
694#endif
695
696}
Note: See TracBrowser for help on using the repository browser.