source: trunk/wx/xmission.cc @ 2615

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

use inttypes.h instead of stdint.h

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