source: trunk/wx/xmission.cc @ 2609

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

reduce flicker in the torrent list

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