(file) Return to btdownloadgui.py CVS log (file) (dir) Up to [Development] / bittornado

   1 theshadow 1.1 #!/usr/bin/env python
   2               
   3               # Written by Bram Cohen and Myers Carpenter
   4               # Modifications by various people
   5               # see LICENSE.txt for license information
   6               
   7 theshadow 1.2 from BitTornado import PSYCO
   8 theshadow 1.1 if PSYCO.psyco:
   9                   try:
  10                       import psyco
  11                       assert psyco.__version__ >= 0x010100f0
  12                       psyco.full()
  13                   except:
  14                       pass
  15               
  16 theshadow 1.68 from sys import argv, version, exit
  17 theshadow 1.1  assert version >= '2', "Install Python 2.0 or greater"
  18                
  19 theshadow 1.26 try:
  20                    from wxPython.wx import *
  21                except:
  22                    print 'wxPython is either not installed or has not been installed properly.'
  23 theshadow 1.68     exit(1)
  24 theshadow 1.7  from BitTornado.download_bt1 import BT1Download, defaults, parse_params, get_usage, get_response
  25 theshadow 1.51 from BitTornado.RawServer import RawServer, UPnP_ERROR
  26 theshadow 1.7  from random import seed
  27                from socket import error as socketerror
  28 theshadow 1.2  from BitTornado.ConnChoice import *
  29                from BitTornado.ConfigReader import configReader
  30 theshadow 1.31 from BitTornado.bencode import bencode, bdecode
  31 theshadow 1.35 from BitTornado.natpunch import UPnP_test
  32 theshadow 1.1  from threading import Event, Thread
  33                from os.path import *
  34                from os import getcwd
  35 theshadow 1.58 from time import strftime, time, localtime, sleep
  36 theshadow 1.38 from BitTornado.clock import clock
  37 theshadow 1.1  from webbrowser import open_new
  38                from traceback import print_exc
  39                from StringIO import StringIO
  40                from sha import sha
  41                import re
  42                import sys, os
  43 theshadow 1.22 from BitTornado import version, createPeerID, report_email
  44 theshadow 1.1  
  45 theshadow 1.13 try:
  46                    True
  47                except:
  48                    True = 1
  49                    False = 0
  50 theshadow 1.1  
  51 theshadow 1.13 PROFILER = False
  52 theshadow 1.30 WXPROFILER = False
  53 theshadow 1.1  
  54 theshadow 1.60 try:
  55                    wxFULL_REPAINT_ON_RESIZE
  56                except:
  57                    wxFULL_REPAINT_ON_RESIZE = 0        # fix for wx pre-2.5
  58                
  59 theshadow 1.32 # Note to packagers: edit OLDICONPATH in BitTornado/ConfigDir.py
  60 theshadow 1.1  
  61                def hours(n):
  62                    if n == 0:
  63 theshadow 1.42         return 'download complete'
  64 theshadow 1.50     try:
  65                        n = int(n)
  66                        assert n >= 0 and n < 5184000  # 60 days
  67                    except:
  68 theshadow 1.1          return '<unknown>'
  69 theshadow 1.50     m, s = divmod(n, 60)
  70                    h, m = divmod(m, 60)
  71 theshadow 1.1      if h > 0:
  72 theshadow 1.50         return '%d hour(s) %02d min %02d sec' % (h, m, s)
  73 theshadow 1.1      else:
  74 theshadow 1.50         return '%d min %02d sec' % (m, s)
  75 theshadow 1.1  
  76                def size_format(s):
  77                    if (s < 1024):
  78                        r = str(s) + 'B'
  79                    elif (s < 1048576):
  80                        r = str(int(s/1024)) + 'KiB'
  81                    elif (s < 1073741824L):
  82                        r = str(int(s/1048576)) + 'MiB'
  83                    elif (s < 1099511627776L):
  84                        r = str(int((s/1073741824.0)*100.0)/100.0) + 'GiB'
  85                    else:
  86                        r = str(int((s/1099511627776.0)*100.0)/100.0) + 'TiB'
  87                    return(r)
  88                
  89                def comma_format(s):
  90                    r = str(s)
  91                    for i in range(len(r)-3, 0, -3):
  92                        r = r[:i]+','+r[i:]
  93                    return(r)
  94                
  95 theshadow 1.32 hexchars = '0123456789abcdef'
  96                hexmap = []
  97 theshadow 1.47 for i in xrange(256):
  98 theshadow 1.32     x = hexchars[(i&0xF0)/16]+hexchars[i&0x0F]
  99                    hexmap.append(x)
 100                
 101                def tohex(s):
 102                    r = []
 103                    for c in s:
 104                        r.append(hexmap[ord(c)])
 105                    return ''.join(r)
 106                
 107 theshadow 1.1  wxEVT_INVOKE = wxNewEventType()
 108                
 109                def EVT_INVOKE(win, func):
 110                    win.Connect(-1, -1, wxEVT_INVOKE, func)
 111                
 112                class InvokeEvent(wxPyEvent):
 113                    def __init__(self, func = None, args = None, kwargs = None):
 114                        wxPyEvent.__init__(self)
 115                        self.SetEventType(wxEVT_INVOKE)
 116                        self.func = func
 117                        self.args = args
 118                        self.kwargs = kwargs
 119                
 120                
 121                
 122                class DownloadInfoFrame:
 123                    def __init__(self, flag, configfile):
 124 theshadow 1.61         self._errorwindow = None
 125 theshadow 1.1          try:
 126 theshadow 1.32             self.FONT = configfile.config['gui_font']
 127 theshadow 1.13             self.default_font = wxFont(self.FONT, wxDEFAULT, wxNORMAL, wxNORMAL, False)
 128 theshadow 1.56             frame = wxFrame(None, -1, 'BitTorrent ' + version + ' download',
 129                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
 130 theshadow 1.1              self.flag = flag
 131 theshadow 1.8              self.configfile = configfile
 132 theshadow 1.32             self.configfileargs = configfile.config
 133 theshadow 1.1              self.uiflag = Event()
 134 theshadow 1.13             self.fin = False
 135 theshadow 1.1              self.aboutBox = None
 136                            self.detailBox = None
 137                            self.advBox = None
 138                            self.creditsBox = None
 139                            self.statusIconHelpBox = None
 140                            self.reannouncelast = 0
 141                            self.spinlock = 0
 142                            self.scrollock = 0
 143                            self.lastError = 0
 144 theshadow 1.37             self.spewwait = clock()
 145 theshadow 1.1              self.config = None
 146                            self.updateSpinnerFlag = 0
 147                            self.updateSliderFlag = 0
 148                            self.statusIconValue = ' '
 149                            self.iconized = 0
 150 theshadow 1.23             self.taskbaricon = False
 151 theshadow 1.1              self.checking = None
 152                            self.activity = 'Starting up...'
 153 theshadow 1.13             self.firstupdate = True
 154                            self.shuttingdown = False
 155                            self.ispaused = False
 156 theshadow 1.1              self.bgalloc_periods = 0
 157                            self.gui_fractiondone = None
 158                            self.fileList = None
 159                            self.lastexternalannounce = ''
 160 theshadow 1.13             self.refresh_details = False
 161 theshadow 1.1              self.lastuploadsettings = 0
 162 theshadow 1.41             self.old_download = 0
 163                            self.old_upload = 0
 164                            self.old_ratesettings = None
 165                            self.current_ratesetting = None
 166 theshadow 1.47             self.gaugemode = None
 167 theshadow 1.63             self.autorate = False
 168 theshadow 1.41             
 169 theshadow 1.1              self.filename = None
 170 theshadow 1.17             self.dow = None
 171 theshadow 1.1              if sys.platform == 'win32':
 172                                self.invokeLaterEvent = InvokeEvent()
 173                                self.invokeLaterList = []
 174                
 175                            wxInitAllImageHandlers()
 176 theshadow 1.32             self.basepath = self.configfile.getIconDir()
 177 theshadow 1.75             self.icon = wxIcon(os.path.join(self.basepath,'icon_bt.ico'), wxBITMAP_TYPE_ICO)
 178 theshadow 1.80             self.finicon = wxIcon(os.path.join(self.basepath,'icon_done.ico'), wxBITMAP_TYPE_ICO)
 179 theshadow 1.75             self.statusIconFiles={
 180                                'startup':os.path.join(self.basepath,'white.ico'),
 181                                'disconnected':os.path.join(self.basepath,'black.ico'),
 182                                'noconnections':os.path.join(self.basepath,'red.ico'),
 183                                'nocompletes':os.path.join(self.basepath,'blue.ico'),
 184                                'noincoming':os.path.join(self.basepath,'yellow.ico'),
 185                                'allgood':os.path.join(self.basepath,'green.ico'),
 186 theshadow 1.1                  }
 187 theshadow 1.75             self.statusIcons={}
 188 theshadow 1.1              self.filestatusIcons = wxImageList(16, 16)
 189 theshadow 1.32             self.filestatusIcons.Add(wxBitmap(os.path.join(self.basepath,'black1.ico'),wxBITMAP_TYPE_ICO))
 190                            self.filestatusIcons.Add(wxBitmap(os.path.join(self.basepath,'yellow1.ico'), wxBITMAP_TYPE_ICO))
 191                            self.filestatusIcons.Add(wxBitmap(os.path.join(self.basepath,'green1.ico'), wxBITMAP_TYPE_ICO))
 192 theshadow 1.1  
 193 theshadow 1.32             self.allocbuttonBitmap = wxBitmap(os.path.join(self.basepath,'alloc.gif'), wxBITMAP_TYPE_GIF)
 194 theshadow 1.1  
 195 theshadow 1.37             self.starttime = clock()
 196 theshadow 1.1  
 197                            self.frame = frame
 198 theshadow 1.75             try:
 199 theshadow 1.1                  self.frame.SetIcon(self.icon)
 200 theshadow 1.75             except:
 201                                pass
 202 theshadow 1.1  
 203                            panel = wxPanel(frame, -1)
 204 theshadow 1.28             self.bgcolor = panel.GetBackgroundColour()
 205 theshadow 1.1  
 206 theshadow 1.13             def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
 207 theshadow 1.1                  x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT)
 208                                x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline))
 209                                if color is not None:
 210                                    x.SetForegroundColour(color)
 211                                return x
 212                
 213                            colSizer = wxFlexGridSizer(cols = 1, vgap = 3)
 214                
 215                            border = wxBoxSizer(wxHORIZONTAL)
 216                            border.Add(colSizer, 1, wxEXPAND | wxALL, 4)
 217                            panel.SetSizer(border)
 218 theshadow 1.13             panel.SetAutoLayout(True)
 219 theshadow 1.1  
 220                            topboxsizer = wxFlexGridSizer(cols = 3, vgap = 0)
 221                            topboxsizer.AddGrowableCol (0)
 222                
 223                            fnsizer = wxFlexGridSizer(cols = 1, vgap = 0)
 224                            fnsizer.AddGrowableCol (0)
 225                            fnsizer.AddGrowableRow (1)
 226                
 227                            fileNameText = StaticText('', self.FONT+4)
 228                            fnsizer.Add(fileNameText, 1, wxALIGN_BOTTOM|wxEXPAND)
 229                            self.fileNameText = fileNameText
 230                
 231                            fnsizer2 = wxFlexGridSizer(cols = 8, vgap = 0)
 232                            fnsizer2.AddGrowableCol (0)
 233                
 234                            fileSizeText = StaticText('')
 235                            fnsizer2.Add(fileSizeText, 1, wxALIGN_BOTTOM|wxEXPAND)
 236                            self.fileSizeText = fileSizeText
 237                
 238 theshadow 1.13             fileDetails = StaticText('Details', self.FONT, True, 'Blue')
 239 theshadow 1.1              fnsizer2.Add(fileDetails, 0, wxALIGN_BOTTOM)                                     
 240                
 241                            fnsizer2.Add(StaticText('  '))
 242                
 243 theshadow 1.13             advText = StaticText('Advanced', self.FONT, True, 'Blue')
 244 theshadow 1.1              fnsizer2.Add(advText, 0, wxALIGN_BOTTOM)
 245                            fnsizer2.Add(StaticText('  '))
 246                
 247 theshadow 1.13             prefsText = StaticText('Prefs', self.FONT, True, 'Blue')
 248 theshadow 1.1              fnsizer2.Add(prefsText, 0, wxALIGN_BOTTOM)
 249                            fnsizer2.Add(StaticText('  '))
 250                
 251 theshadow 1.13             aboutText = StaticText('About', self.FONT, True, 'Blue')
 252 theshadow 1.1              fnsizer2.Add(aboutText, 0, wxALIGN_BOTTOM)
 253                
 254                            fnsizer2.Add(StaticText('  '))
 255                            fnsizer.Add(fnsizer2,0,wxEXPAND)
 256                            topboxsizer.Add(fnsizer,0,wxEXPAND)
 257                            topboxsizer.Add(StaticText('  '))
 258                
 259                            self.statusIcon = wxEmptyBitmap(32,32)
 260                            statidata = wxMemoryDC()
 261                            statidata.SelectObject(self.statusIcon)
 262                            statidata.SetPen(wxTRANSPARENT_PEN)
 263 theshadow 1.53             statidata.SetBrush(wxBrush(self.bgcolor,wxSOLID))
 264 theshadow 1.1              statidata.DrawRectangle(0,0,32,32)
 265                            self.statusIconPtr = wxStaticBitmap(panel, -1, self.statusIcon)
 266                            topboxsizer.Add(self.statusIconPtr)
 267                
 268                            self.fnsizer = fnsizer
 269                            self.fnsizer2 = fnsizer2
 270                            self.topboxsizer = topboxsizer
 271                            colSizer.Add(topboxsizer, 0, wxEXPAND)
 272                
 273                            self.gauge = wxGauge(panel, -1, range = 1000, style = wxGA_SMOOTH)
 274                            colSizer.Add(self.gauge, 0, wxEXPAND)
 275                
 276                            timeSizer = wxFlexGridSizer(cols = 2)
 277                            timeSizer.Add(StaticText('Time elapsed / estimated : '))
 278                            self.timeText = StaticText(self.activity+'                    ')
 279                            timeSizer.Add(self.timeText)
 280                            timeSizer.AddGrowableCol(1)
 281                            colSizer.Add(timeSizer)
 282                
 283                            destSizer = wxFlexGridSizer(cols = 2, hgap = 8)
 284                            self.fileDestLabel = StaticText('Download to:')
 285 theshadow 1.1              destSizer.Add(self.fileDestLabel)
 286                            self.fileDestText = StaticText('')
 287                            destSizer.Add(self.fileDestText, flag = wxEXPAND)
 288                            destSizer.AddGrowableCol(1)
 289                            colSizer.Add(destSizer, flag = wxEXPAND)
 290                            self.destSizer = destSizer
 291                
 292                            statSizer = wxFlexGridSizer(cols = 3, hgap = 8)
 293                
 294                            self.ratesSizer = wxFlexGridSizer(cols = 2)
 295                            self.infoSizer = wxFlexGridSizer(cols = 2)
 296                
 297                            self.ratesSizer.Add(StaticText('   Download rate: '))
 298                            self.downRateText = StaticText('0 kB/s       ')
 299                            self.ratesSizer.Add(self.downRateText, flag = wxEXPAND)
 300                
 301                            self.downTextLabel = StaticText('Downloaded: ')
 302                            self.infoSizer.Add(self.downTextLabel)
 303                            self.downText = StaticText('0.00 MiB        ')
 304                            self.infoSizer.Add(self.downText, flag = wxEXPAND)
 305                
 306 theshadow 1.1              self.ratesSizer.Add(StaticText('   Upload rate: '))
 307                            self.upRateText = StaticText('0 kB/s       ')
 308                            self.ratesSizer.Add(self.upRateText, flag = wxEXPAND)
 309                
 310                            self.upTextLabel = StaticText('Uploaded: ')
 311                            self.infoSizer.Add(self.upTextLabel)
 312                            self.upText = StaticText('0.00 MiB        ')
 313                            self.infoSizer.Add(self.upText, flag = wxEXPAND)
 314                
 315                            shareSizer = wxFlexGridSizer(cols = 2, hgap = 8)
 316                            shareSizer.Add(StaticText('Share rating:'))
 317                            self.shareRatingText = StaticText('')
 318                            shareSizer.AddGrowableCol(1)
 319                            shareSizer.Add(self.shareRatingText, flag = wxEXPAND)
 320                
 321                            statSizer.Add(self.ratesSizer)
 322                            statSizer.Add(self.infoSizer)
 323                            statSizer.Add(shareSizer, flag = wxALIGN_CENTER_VERTICAL)
 324                            colSizer.Add (statSizer)
 325                
 326                            torrentSizer = wxFlexGridSizer(cols = 1)
 327 theshadow 1.1              self.peerStatusText = StaticText('')
 328                            torrentSizer.Add(self.peerStatusText, 0, wxEXPAND)
 329                            self.seedStatusText = StaticText('')
 330                            torrentSizer.Add(self.seedStatusText, 0, wxEXPAND)
 331                            torrentSizer.AddGrowableCol(0)
 332                            colSizer.Add(torrentSizer, 0, wxEXPAND)
 333                            self.torrentSizer = torrentSizer
 334                
 335                            self.errorTextSizer = wxFlexGridSizer(cols = 1)
 336 theshadow 1.13             self.errorText = StaticText('', self.FONT, False, 'Red')
 337 theshadow 1.1              self.errorTextSizer.Add(self.errorText, 0, wxEXPAND)
 338                            colSizer.Add(self.errorTextSizer, 0, wxEXPAND)
 339                
 340                            cancelSizer=wxGridSizer(cols = 2, hgap = 40)
 341                            self.pauseButton = wxButton(panel, -1, 'Pause')
 342                            cancelSizer.Add(self.pauseButton, 0, wxALIGN_CENTER)
 343                
 344                            self.cancelButton = wxButton(panel, -1, 'Cancel')
 345                            cancelSizer.Add(self.cancelButton, 0, wxALIGN_CENTER)
 346                            colSizer.Add(cancelSizer, 0, wxALIGN_CENTER)
 347                
 348                            # Setting options
 349                
 350                            slideSizer = wxFlexGridSizer(cols = 7, hgap = 0, vgap = 5)
 351                
 352                            # dropdown
 353                
 354                            self.connChoiceLabel = StaticText('Settings for ')
 355                            slideSizer.Add (self.connChoiceLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL)
 356                            self.connChoice = wxChoice (panel, -1, (-1, -1), (self.FONT*11, -1),
 357                                                        choices = connChoiceList)
 358 theshadow 1.1              self.connChoice.SetFont(self.default_font)
 359                            self.connChoice.SetSelection(0)
 360                            slideSizer.Add (self.connChoice, 0, wxALIGN_CENTER)
 361                            self.rateSpinnerLabel = StaticText(' Upload rate (kB/s) ')
 362                            slideSizer.Add (self.rateSpinnerLabel, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
 363                
 364                            # max upload rate
 365                
 366                            self.rateSpinner = wxSpinCtrl (panel, -1, "", (-1,-1), (50, -1))
 367                            self.rateSpinner.SetFont(self.default_font)
 368                            self.rateSpinner.SetRange(0,5000)
 369                            self.rateSpinner.SetValue(0)
 370                            slideSizer.Add (self.rateSpinner, 0, wxALIGN_CENTER|wxALIGN_CENTER_VERTICAL)
 371                
 372                            self.rateLowerText = StaticText('  %5d' % (0))
 373                            self.rateUpperText = StaticText('%5d' % (5000))
 374                            self.rateslider = wxSlider(panel, -1, 0, 0, 5000, (-1, -1), (80, -1))
 375                
 376                            slideSizer.Add(self.rateLowerText, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
 377                            slideSizer.Add(self.rateslider,    0, wxALIGN_CENTER|wxALIGN_CENTER_VERTICAL)
 378                            slideSizer.Add(self.rateUpperText, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL)
 379 theshadow 1.1  
 380                            slideSizer.Add(StaticText(''), 0, wxALIGN_LEFT)
 381                
 382 theshadow 1.13             self.bgallocText = StaticText('', self.FONT+2, False, 'Red')
 383 theshadow 1.1              slideSizer.Add(self.bgallocText, 0, wxALIGN_LEFT)
 384                
 385                            # max uploads
 386                
 387                            self.connSpinnerLabel = StaticText(' Max uploads ')
 388                            slideSizer.Add (self.connSpinnerLabel, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
 389                            self.connSpinner = wxSpinCtrl (panel, -1, "", (-1,-1), (50, -1))
 390                            self.connSpinner.SetFont(self.default_font)
 391                            self.connSpinner.SetRange(4,100)
 392                            self.connSpinner.SetValue(4)
 393                            slideSizer.Add (self.connSpinner, 0, wxALIGN_CENTER|wxALIGN_CENTER_VERTICAL)
 394                
 395                            self.connLowerText = StaticText('  %5d' % (4))
 396                            self.connUpperText = StaticText('%5d' % (100))
 397                            self.connslider = wxSlider(panel, -1, 4, 4, 100, (-1, -1), (80, -1))
 398                
 399                            slideSizer.Add(self.connLowerText, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)
 400                            slideSizer.Add(self.connslider,    0, wxALIGN_CENTER|wxALIGN_CENTER_VERTICAL)
 401                            slideSizer.Add(self.connUpperText, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL)
 402                
 403                            colSizer.Add(slideSizer, 1, wxALL|wxALIGN_CENTER|wxEXPAND, 0)
 404 theshadow 1.1  
 405                            self.unlimitedLabel = StaticText('0 kB/s means unlimited. Tip: your download rate is proportional to your upload rate', self.FONT-2)
 406                            colSizer.Add(self.unlimitedLabel, 0, wxALIGN_CENTER)
 407                
 408 theshadow 1.26             self.priorityIDs = [wxNewId(),wxNewId(),wxNewId(),wxNewId()]
 409 theshadow 1.29             self.prioritycolors = [ wxColour(160,160,160),
 410                                                    wxColour(255,64,0),
 411 theshadow 1.27                                     wxColour(0,0,0),
 412 theshadow 1.29                                     wxColour(64,64,255) ]
 413 theshadow 1.27 
 414 theshadow 1.26 
 415 theshadow 1.1              EVT_LEFT_DOWN(aboutText, self.about)
 416                            EVT_LEFT_DOWN(fileDetails, self.details)
 417                            EVT_LEFT_DOWN(self.statusIconPtr,self.statusIconHelp)
 418                            EVT_LEFT_DOWN(advText, self.advanced)
 419                            EVT_LEFT_DOWN(prefsText, self.openConfigMenu)
 420                            EVT_CLOSE(frame, self.done)
 421                            EVT_BUTTON(frame, self.pauseButton.GetId(), self.pause)
 422                            EVT_BUTTON(frame, self.cancelButton.GetId(), self.done)
 423                            EVT_INVOKE(frame, self.onInvoke)
 424                            EVT_SCROLL(self.rateslider, self.onRateScroll)
 425                            EVT_SCROLL(self.connslider, self.onConnScroll)
 426                            EVT_CHOICE(self.connChoice, -1, self.onConnChoice)
 427                            EVT_SPINCTRL(self.connSpinner, -1, self.onConnSpinner)
 428                            EVT_SPINCTRL(self.rateSpinner, -1, self.onRateSpinner)
 429                            if (sys.platform == 'win32'):
 430 theshadow 1.23                 self.frame.tbicon = wxTaskBarIcon()
 431 theshadow 1.1                  EVT_ICONIZE(self.frame, self.onIconify)
 432 theshadow 1.23                 EVT_TASKBAR_LEFT_DCLICK(self.frame.tbicon, self.onTaskBarActivate)
 433                                EVT_TASKBAR_RIGHT_UP(self.frame.tbicon, self.onTaskBarMenu)
 434                                EVT_MENU(self.frame.tbicon, self.TBMENU_RESTORE, self.onTaskBarActivate)
 435                                EVT_MENU(self.frame.tbicon, self.TBMENU_CLOSE, self.done)
 436 theshadow 1.1              colSizer.AddGrowableCol (0)
 437                            colSizer.AddGrowableRow (6)
 438                            self.frame.Show()
 439                            border.Fit(panel)
 440                            self.frame.Fit()
 441                            self.panel = panel
 442                            self.border = border
 443                            self.addwidth = aboutText.GetBestSize().GetWidth() + fileDetails.GetBestSize().GetWidth() + (self.FONT*14)
 444                            self.fnsizer = fnsizer
 445                            self.colSizer = colSizer
 446                            minsize = self.colSizer.GetSize()
 447                            minsize.SetWidth (minsize.GetWidth())
 448                            minsize.SetHeight (minsize.GetHeight())
 449                            self.colSizer.SetMinSize (minsize)
 450                            self.colSizer.Fit(self.frame)
 451                            colSizer.Fit(frame)
 452                        except:
 453                            self.exception()
 454                
 455                    if sys.platform == 'win32':     # windows-only optimization
 456                        def onInvoke(self, event):
 457 theshadow 1.1              while self.invokeLaterList:
 458                                func,args,kwargs = self.invokeLaterList[0]
 459                                if self.uiflag.isSet():
 460                                    return
 461                                try:
 462                                    apply(func,args,kwargs)
 463                                except:
 464                                    self.exception()
 465                                del self.invokeLaterList[0]
 466                
 467                        def invokeLater(self, func, args = [], kwargs = {}):
 468                            if not self.uiflag.isSet():
 469                                self.invokeLaterList.append((func,args,kwargs))
 470                                if len(self.invokeLaterList) == 1:
 471                                    wxPostEvent(self.frame, self.invokeLaterEvent)
 472                    else:
 473                        def onInvoke(self, event):
 474                            if not self.uiflag.isSet():
 475                                try:
 476                                    apply(event.func, event.args, event.kwargs)
 477                                except:
 478 theshadow 1.1                      self.exception()
 479                
 480                        def invokeLater(self, func, args = [], kwargs = {}):
 481                            if not self.uiflag.isSet():
 482                                wxPostEvent(self.frame, InvokeEvent(func, args, kwargs))
 483                
 484                
 485 theshadow 1.75     def getStatusIcon(self, name, bitmap=False):
 486                        if self.statusIcons.has_key(name):
 487                            i = self.statusIcons[name]
 488                            if type(i)  == type(self.icon) and not bitmap:
 489                                return i
 490                        if bitmap:
 491                            i = wxBitmap(self.statusIconFiles[name], wxBITMAP_TYPE_ICO)
 492                        else:
 493                            i = wxIcon(self.statusIconFiles[name], wxBITMAP_TYPE_ICO)
 494                        self.statusIcons[name] = i
 495                        return i
 496                
 497                
 498 theshadow 1.1      def setStatusIcon(self, name):
 499 theshadow 1.75         if name == self.statusIconValue:
 500                            return
 501                        self.statusIconValue = name
 502                        statidata = wxMemoryDC()
 503                        statidata.SelectObject(self.statusIcon)
 504                        statidata.BeginDrawing()
 505                        try:
 506                            statidata.DrawIcon(self.getStatusIcon(name),0,0)
 507                        except:
 508                            statidata.DrawBitmap(self.getStatusIcon(name,True),0,0,True)
 509                        statidata.EndDrawing()
 510                        statidata.SelectObject(wxNullBitmap)
 511                        self.statusIconPtr.Refresh()
 512 theshadow 1.1  
 513                
 514                    def createStatusIcon(self, name):
 515                        iconbuffer = wxEmptyBitmap(32,32)
 516                        bbdata = wxMemoryDC()
 517                        bbdata.SelectObject(iconbuffer)
 518                        bbdata.SetPen(wxTRANSPARENT_PEN)
 519 theshadow 1.28         bbdata.SetBrush(wxBrush(self.bgcolor,wxSOLID))
 520 theshadow 1.1          bbdata.DrawRectangle(0,0,32,32)
 521 theshadow 1.75         try:
 522                            bbdata.DrawIcon(self.getStatusIcon(name),0,0)
 523                        except:
 524                            bbdata.DrawBitmap(self.getStatusIcon(name,True),0,0,True)
 525 theshadow 1.1          return iconbuffer
 526                
 527                
 528 theshadow 1.47     def setgaugemode(self, selection):
 529                        if selection is None:
 530                            selection = self.gaugemode
 531                        elif selection == self.gaugemode:
 532                            return
 533                        else:
 534                            self.gaugemode = selection
 535                        if selection < 0:
 536                            self.gauge.SetForegroundColour(self.configfile.getcheckingcolor())
 537                            self.gauge.SetBackgroundColour(wxSystemSettings_GetColour(wxSYS_COLOUR_MENU))
 538                        elif selection == 0:
 539                            self.gauge.SetForegroundColour(self.configfile.getdownloadcolor())
 540                            self.gauge.SetBackgroundColour(wxSystemSettings_GetColour(wxSYS_COLOUR_MENU))
 541                        else:
 542                            self.gauge.SetForegroundColour(self.configfile.getseedingcolor())
 543                            self.gauge.SetBackgroundColour(self.configfile.getdownloadcolor())
 544                
 545                
 546 theshadow 1.1      def onIconify(self, evt):
 547                        try:
 548 theshadow 1.32             if self.configfileargs['win32_taskbar_icon']:
 549 theshadow 1.81                 if self.fin:
 550 theshadow 1.80                     self.frame.tbicon.SetIcon(self.finicon, "BitTorrent")
 551                                else:
 552                                    self.frame.tbicon.SetIcon(self.icon, "BitTorrent")
 553 theshadow 1.1                  self.frame.Hide()
 554 theshadow 1.23                 self.taskbaricon = True
 555 theshadow 1.1              else:
 556 theshadow 1.80                 return
 557 theshadow 1.1          except:
 558                            self.exception()
 559                
 560                
 561                    def onTaskBarActivate(self, evt):
 562                        try:
 563                            if self.frame.IsIconized():
 564 theshadow 1.13                 self.frame.Iconize(False)
 565 theshadow 1.1              if not self.frame.IsShown():
 566 theshadow 1.13                 self.frame.Show(True)
 567 theshadow 1.1                  self.frame.Raise()
 568 theshadow 1.23             self.frame.tbicon.RemoveIcon()
 569                            self.taskbaricon = False
 570 theshadow 1.71         except wxPyDeadObjectError:
 571                            pass
 572 theshadow 1.1          except:
 573                            self.exception()
 574                
 575                    TBMENU_RESTORE = 1000
 576                    TBMENU_CLOSE   = 1001
 577                
 578                    def onTaskBarMenu(self, evt):
 579                        menu = wxMenu()
 580                        menu.Append(self.TBMENU_RESTORE, "Restore BitTorrent")
 581                        menu.Append(self.TBMENU_CLOSE,   "Close")
 582                        self.frame.tbicon.PopupMenu(menu)
 583                        menu.Destroy()
 584                
 585                
 586                    def _try_get_config(self):
 587                        if self.config is None:
 588 theshadow 1.16             try:
 589                                self.config = self.dow.getConfig()
 590                            except:
 591                                pass
 592 theshadow 1.1          return self.config != None
 593                
 594                    def onRateScroll(self, event):
 595                        try:
 596 theshadow 1.64             if self.autorate:
 597                                return
 598                            if not self._try_get_config():
 599                                return
 600 theshadow 1.1              if (self.scrollock == 0):
 601                                self.scrollock = 1
 602                                self.updateSpinnerFlag = 1
 603 theshadow 1.15                 self.dow.setUploadRate(self.rateslider.GetValue()
 604 theshadow 1.1                              * connChoices[self.connChoice.GetSelection()]['rate'].get('div',1))
 605                                self.scrollock = 0
 606                        except:
 607                            self.exception()
 608                
 609                    def onConnScroll(self, event):
 610                        try:
 611 theshadow 1.63             if self.autorate:
 612                                return
 613 theshadow 1.1              if not self._try_get_config():
 614                                return
 615                            self.connSpinner.SetValue (self.connslider.GetValue ())
 616                            self.dow.setConns(self.connslider.GetValue())
 617                        except:
 618                            self.exception()
 619                
 620 theshadow 1.63     def onRateSpinner(self, event = None):
 621 theshadow 1.1          try:
 622 theshadow 1.63             if self.autorate:
 623                                return
 624 theshadow 1.1              if not self._try_get_config():
 625                                return
 626                            if (self.spinlock == 0):
 627                                self.spinlock = 1
 628                                spinnerValue = self.rateSpinner.GetValue()
 629                                div = connChoices[self.connChoice.GetSelection()]['rate'].get('div',1)
 630                                if div > 1:
 631 theshadow 1.15                     if spinnerValue > (self.config['max_upload_rate']):
 632 theshadow 1.1                          round_up = div - 1
 633                                    else:
 634                                        round_up = 0
 635                                    newValue = int((spinnerValue + round_up) / div) * div
 636                                    if newValue != spinnerValue:
 637                                        self.rateSpinner.SetValue(newValue)
 638                                else:
 639                                    newValue = spinnerValue
 640 theshadow 1.15                 self.dow.setUploadRate(newValue)
 641 theshadow 1.1                  self.updateSliderFlag = 1
 642                                self.spinlock = 0
 643                        except:
 644                            self.exception()
 645                
 646 theshadow 1.48     def onDownRateSpinner(self, event=None):
 647 theshadow 1.40         try:
 648                            if not self._try_get_config():
 649                                return
 650                            spinnerValue = self.downrateSpinner.GetValue()
 651                            self.dow.setDownloadRate(self.downrateSpinner.GetValue())
 652                        except:
 653                            self.exception()
 654                
 655 theshadow 1.63     def onConnSpinner(self, event = None):
 656 theshadow 1.1          try:
 657 theshadow 1.63             if self.autorate:
 658                                return
 659 theshadow 1.1              if not self._try_get_config():
 660                                return
 661                            self.connslider.SetValue (self.connSpinner.GetValue())
 662                            self.dow.setConns(self.connslider.GetValue())
 663                        except:
 664                            self.exception()
 665                
 666 theshadow 1.41     def onConnChoice(self, event, cons=None, rate=None):
 667 theshadow 1.1          try:
 668                            if not self._try_get_config():
 669                                return
 670                            num = self.connChoice.GetSelection()
 671 theshadow 1.63             choice = connChoices[num]
 672                            if choice.has_key('super-seed'):  # selecting super-seed is now a toggle
 673                                self.dow.set_super_seed()     # one way change, don't go back
 674                                self.connChoice.SetSelection(self.lastuploadsettings)
 675 theshadow 1.1                  return
 676                            self.lastuploadsettings = num
 677 theshadow 1.41             self.current_ratesetting = self.connChoice.GetStringSelection()
 678                            if rate is None:
 679 theshadow 1.63                 rate = choice['rate']['def']
 680                            self.rateSpinner.SetRange (choice['rate']['min'],
 681                                                   choice['rate']['max'])
 682                            self.rateSpinner.SetValue(rate)
 683                            self.rateslider.SetRange(
 684                                choice['rate']['min']/choice['rate'].get('div',1),
 685                                choice['rate']['max']/choice['rate'].get('div',1))
 686                            self.rateslider.SetValue (rate/choice['rate'].get('div',1))
 687                            self.rateLowerText.SetLabel ('  %d' % (choice['rate']['min']))
 688                            self.rateUpperText.SetLabel ('%d' % (choice['rate']['max']))
 689 theshadow 1.41             if cons is None:
 690 theshadow 1.63                 cons = choice['conn']['def']
 691                            self.connSpinner.SetRange (choice['conn']['min'],
 692                                                       choice['conn']['max'])
 693 theshadow 1.41             self.connSpinner.SetValue (cons)
 694 theshadow 1.63             self.connslider.SetRange (choice['conn']['min'],
 695                                                      choice['conn']['max'])
 696 theshadow 1.41             self.connslider.SetValue (cons)
 697 theshadow 1.63             self.connLowerText.SetLabel ('  %d' % (choice['conn']['min']))
 698                            self.connUpperText.SetLabel ('%d' % (choice['conn']['max']))
 699 theshadow 1.1              self.onConnScroll (0)
 700                            self.onRateScroll (0)
 701 theshadow 1.63             self.dow.setInitiate(choice.get('initiate', 40))
 702                            if choice.has_key('automatic'):
 703                                if not self.autorate:
 704                                    self.autorate = True
 705 theshadow 1.65                     self.rateSpinner.Enable(False)
 706                                    self.connSpinner.Enable(False)
 707 theshadow 1.63                     self.dow.setUploadRate(-1)
 708                            else:
 709                                if self.autorate:
 710                                    self.autorate = False
 711 theshadow 1.65                     self.rateSpinner.Enable(True)
 712                                    self.connSpinner.Enable(True)
 713 theshadow 1.63                     self.onRateSpinner()
 714                                    self.onConnSpinner()
 715 theshadow 1.1          except:
 716                            self.exception()
 717                
 718                
 719                    def about(self, event):
 720                        try:
 721                            if (self.aboutBox is not None):
 722                                try:
 723                                    self.aboutBox.Close ()
 724                                except wxPyDeadObjectError, e:
 725                                    self.aboutBox = None
 726                
 727 theshadow 1.56             self.aboutBox = wxFrame(None, -1, 'About BitTorrent', size = (1,1),
 728                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
 729 theshadow 1.75             try:
 730 theshadow 1.1                  self.aboutBox.SetIcon(self.icon)
 731 theshadow 1.75             except:
 732                                pass
 733 theshadow 1.1  
 734                            panel = wxPanel(self.aboutBox, -1)
 735                
 736 theshadow 1.13             def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
 737 theshadow 1.1                  x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT)
 738                                x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline))
 739                                if color is not None:
 740                                    x.SetForegroundColour(color)
 741                                return x
 742                
 743                            colSizer = wxFlexGridSizer(cols = 1, vgap = 3)
 744                
 745                            titleSizer = wxBoxSizer(wxHORIZONTAL)
 746                            aboutTitle = StaticText('BitTorrent ' + version + '  ', self.FONT+4)
 747                            titleSizer.Add (aboutTitle)
 748 theshadow 1.13             linkDonate = StaticText('Donate to Bram', self.FONT, True, 'Blue')
 749 theshadow 1.1              titleSizer.Add (linkDonate, 1, wxALIGN_BOTTOM&wxEXPAND)
 750                            colSizer.Add(titleSizer, 0, wxEXPAND)
 751                
 752                            colSizer.Add(StaticText('created by Bram Cohen, Copyright 2001-2003,'))
 753                            colSizer.Add(StaticText('experimental version maintained by John Hoffman 2003'))
 754                            colSizer.Add(StaticText('modified from experimental version by Eike Frost 2003'))
 755 theshadow 1.13             credits = StaticText('full credits\n', self.FONT, True, 'Blue')
 756 theshadow 1.1              colSizer.Add(credits);
 757                
 758                            si = ( 'exact Version String: ' + version + '\n' +
 759                                   'Python version: ' + sys.version + '\n' +
 760                                   'wxWindows version: ' + wxVERSION_STRING + '\n' )
 761                            try:
 762                                si += 'Psyco version: ' + hex(psyco.__version__)[2:] + '\n'
 763                            except:
 764                                pass
 765                            colSizer.Add(StaticText(si))
 766                
 767                            babble1 = StaticText(
 768                             'This is an experimental, unofficial build of BitTorrent.\n' +
 769                             'It is Free Software under an MIT-Style license.')
 770 theshadow 1.13             babble2 = StaticText('BitTorrent Homepage (link)', self.FONT, True, 'Blue')
 771                            babble3 = StaticText("TheSHAD0W's Client Homepage (link)", self.FONT, True, 'Blue')
 772                            babble4 = StaticText("Eike Frost's Client Homepage (link)", self.FONT, True, 'Blue')
 773                            babble6 = StaticText('License Terms (link)', self.FONT, True, 'Blue')
 774 theshadow 1.1              colSizer.Add (babble1)
 775                            colSizer.Add (babble2)
 776                            colSizer.Add (babble3)
 777                            colSizer.Add (babble4)
 778                            colSizer.Add (babble6)
 779                
 780                            okButton = wxButton(panel, -1, 'Ok')
 781                            colSizer.Add(okButton, 0, wxALIGN_RIGHT)
 782                            colSizer.AddGrowableCol(0)
 783                
 784                            border = wxBoxSizer(wxHORIZONTAL)
 785                            border.Add(colSizer, 1, wxEXPAND | wxALL, 4)
 786                            panel.SetSizer(border)
 787 theshadow 1.13             panel.SetAutoLayout(True)
 788 theshadow 1.1  
 789                            def donatelink(self):
 790                                Thread(target = open_new('https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=bram@bitconjurer.org&item_name=BitTorrent&amount=5.00&submit=donate')).start()
 791                            EVT_LEFT_DOWN(linkDonate, donatelink)
 792                            def aboutlink(self):
 793                                Thread(target = open_new('http://bitconjurer.org/BitTorrent/')).start()
 794                            EVT_LEFT_DOWN(babble2, aboutlink)
 795                            def shadlink(self):
 796 theshadow 1.6                  Thread(target = open_new('http://www.bittornado.com/')).start()
 797 theshadow 1.1              EVT_LEFT_DOWN(babble3, shadlink)
 798                            def explink(self):
 799                                Thread(target = open_new('http://ei.kefro.st/projects/btclient/')).start()
 800                            EVT_LEFT_DOWN(babble4, explink)
 801                            def licenselink(self):
 802                                Thread(target = open_new('http://ei.kefro.st/projects/btclient/LICENSE.TXT')).start()
 803                            EVT_LEFT_DOWN(babble6, licenselink)
 804                            EVT_LEFT_DOWN(credits, self.credits)
 805                
 806 theshadow 1.83             def closeAbout(e, self = self):
 807                                if self.aboutBox:
 808                                    self.aboutBox.Close()
 809 theshadow 1.1              EVT_BUTTON(self.aboutBox, okButton.GetId(), closeAbout)
 810 theshadow 1.83             def kill(e, self = self):
 811 theshadow 1.80                 try:
 812 theshadow 1.83                     self.aboutBox.RemoveIcon()
 813 theshadow 1.80                 except:
 814                                    pass
 815 theshadow 1.83                 self.aboutBox.Destroy()
 816                                self.aboutBox = None
 817 theshadow 1.1              EVT_CLOSE(self.aboutBox, kill)
 818                
 819 theshadow 1.80             self.aboutBox.Show()
 820 theshadow 1.1              border.Fit(panel)
 821                            self.aboutBox.Fit()
 822                        except:
 823                            self.exception()
 824                
 825                
 826                    def details(self, event):
 827                        try:
 828 theshadow 1.52             if not self.dow or not self.filename:
 829 theshadow 1.17                 return
 830 theshadow 1.1              metainfo = self.dow.getResponse()
 831                            if metainfo is None:
 832                                return
 833                            if metainfo.has_key('announce'):
 834                                announce = metainfo['announce']
 835                            else:
 836                                announce = None
 837                            if metainfo.has_key('announce-list'):
 838                                announce_list = metainfo['announce-list']
 839                            else:
 840                                announce_list = None
 841                            info = metainfo['info']
 842 theshadow 1.32             info_hash = self.dow.infohash
 843 theshadow 1.1              piece_length = info['piece length']
 844 theshadow 1.84             fileselector = self.dow.fileselector
 845 theshadow 1.1  
 846                            if (self.detailBox is not None):
 847                                try:
 848 theshadow 1.83                     self.detailBox.Close()
 849 theshadow 1.1                  except wxPyDeadObjectError, e:
 850                                    self.detailBox = None
 851                
 852 theshadow 1.56             self.detailBox = wxFrame(None, -1, 'Torrent Details ', size = wxSize(405,230),
 853                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
 854 theshadow 1.75             try:
 855 theshadow 1.1                  self.detailBox.SetIcon(self.icon)
 856 theshadow 1.75             except:
 857                                pass
 858 theshadow 1.1  
 859                            panel = wxPanel(self.detailBox, -1, size = wxSize (400,220))
 860                
 861 theshadow 1.13             def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
 862 theshadow 1.1                  x = wxStaticText(panel, -1, text, style = wxALIGN_CENTER_VERTICAL)
 863                                x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline))
 864                                if color is not None:
 865                                    x.SetForegroundColour(color)
 866                                return x
 867                
 868                            colSizer = wxFlexGridSizer(cols = 1, vgap = 3)
 869                            colSizer.AddGrowableCol(0)
 870                
 871                            titleSizer = wxBoxSizer(wxHORIZONTAL)
 872                            aboutTitle = StaticText('Details about ' + self.filename, self.FONT+4)
 873                
 874                            titleSizer.Add (aboutTitle)
 875                            colSizer.Add (titleSizer)
 876                
 877                            detailSizer = wxFlexGridSizer(cols = 2, vgap = 6)
 878                
 879                            if info.has_key('length'):
 880 theshadow 1.26                 fileListID = None
 881 theshadow 1.1                  detailSizer.Add(StaticText('file name :'))
 882                                detailSizer.Add(StaticText(info['name']))
 883                                if info.has_key('md5sum'):
 884                                    detailSizer.Add(StaticText('MD5 hash :'))
 885                                    detailSizer.Add(StaticText(info['md5sum']))
 886                                file_length = info['length']
 887                                name = "file size"
 888                            else:
 889                                detail1Sizer = wxFlexGridSizer(cols = 1, vgap = 6)
 890                                detail1Sizer.Add(StaticText('directory name : ' + info['name']))
 891                                colSizer.Add (detail1Sizer)
 892                                bgallocButton = wxBitmapButton(panel, -1, self.allocbuttonBitmap, size = (52,20))
 893                                def bgalloc(self, frame = self):
 894                                    if frame.dow.storagewrapper is not None:
 895                                        frame.dow.storagewrapper.bgalloc()
 896                                EVT_BUTTON(self.detailBox, bgallocButton.GetId(), bgalloc)
 897                
 898 theshadow 1.28                 bgallocbuttonSizer = wxFlexGridSizer(cols = 4, hgap = 4, vgap = 0)
 899                                bgallocbuttonSizer.Add(StaticText('(right-click to set priority)',self.FONT-1),0,wxALIGN_BOTTOM)
 900 theshadow 1.1                  bgallocbuttonSizer.Add(StaticText('(finish allocation)'), -1, wxALIGN_CENTER_VERTICAL)
 901                                bgallocbuttonSizer.Add(bgallocButton, -1, wxALIGN_CENTER)
 902 theshadow 1.28                 bgallocbuttonSizer.AddGrowableCol(0)
 903                                colSizer.Add(bgallocbuttonSizer, -1, wxEXPAND)
 904 theshadow 1.1  
 905                                file_length = 0
 906                
 907 theshadow 1.26                 fileListID = wxNewId()
 908                                fileList = wxListCtrl(panel, fileListID,
 909                                                      wxPoint(-1,-1), (325,100), wxLC_REPORT)
 910 theshadow 1.1                  self.fileList = fileList
 911                                fileList.SetImageList(self.filestatusIcons, wxIMAGE_LIST_SMALL)
 912                
 913 theshadow 1.13                 fileList.SetAutoLayout (True)
 914 theshadow 1.1                  fileList.InsertColumn(0, "file")
 915                                fileList.InsertColumn(1, "", format=wxLIST_FORMAT_RIGHT, width=55)
 916                                fileList.InsertColumn(2, "")
 917                
 918                                for i in range(len(info['files'])):
 919                                    x = wxListItem()
 920                                    fileList.InsertItem(x)
 921                
 922                                x = 0
 923                                for file in info['files']:
 924                                    path = ' '
 925                                    for item in file['path']:
 926                                        if (path != ''):
 927                                            path = path + "/"
 928                                        path = path + item
 929                                    path += ' (' + str(file['length']) + ')'
 930                                    fileList.SetStringItem(x, 0, path)
 931                                    if file.has_key('md5sum'):
 932                                        fileList.SetStringItem(x, 2, '    [' + str(file['md5sum']) + ']')
 933 theshadow 1.84                     if fileselector:
 934                                        p = fileselector[x]
 935                                        item = self.fileList.GetItem(x)
 936                                        item.SetTextColour(self.prioritycolors[p+1])
 937                                        fileList.SetItem(item)
 938 theshadow 1.1                      x += 1
 939                                    file_length += file['length']
 940                                fileList.SetColumnWidth(0,wxLIST_AUTOSIZE)
 941                                fileList.SetColumnWidth(2,wxLIST_AUTOSIZE)
 942                
 943                                name = 'archive size'
 944                                colSizer.Add(fileList, 1, wxEXPAND)
 945                                colSizer.AddGrowableRow(3)
 946                
 947                            detailSizer.Add(StaticText('info_hash :'),0,wxALIGN_CENTER_VERTICAL)
 948 theshadow 1.32             detailSizer.Add(wxTextCtrl(panel, -1, tohex(info_hash), size = (325, -1), style = wxTE_READONLY))
 949 theshadow 1.1              num_pieces = int((file_length+piece_length-1)/piece_length)
 950                            detailSizer.Add(StaticText(name + ' : '))
 951                            detailSizer.Add(StaticText('%s (%s bytes)' % (size_format(file_length), comma_format(file_length))))
 952                            detailSizer.Add(StaticText('pieces : '))
 953                            if num_pieces > 1:
 954                                detailSizer.Add(StaticText('%i (%s bytes each)' % (num_pieces, comma_format(piece_length))))
 955                            else:
 956                                detailSizer.Add(StaticText('1'))
 957                
 958                            if announce_list is None:
 959                                detailSizer.Add(StaticText('announce url : '),0,wxALIGN_CENTER_VERTICAL)
 960                                detailSizer.Add(wxTextCtrl(panel, -1, announce, size = (325, -1), style = wxTE_READONLY))
 961                            else:
 962                                detailSizer.Add(StaticText(''))
 963                                trackerList = wxListCtrl(panel, -1, wxPoint(-1,-1), (325,75), wxLC_REPORT)
 964 theshadow 1.13                 trackerList.SetAutoLayout (True)
 965 theshadow 1.1                  trackerList.InsertColumn(0, "")
 966                                trackerList.InsertColumn(1, "announce urls")
 967                
 968                                for tier in range(len(announce_list)):
 969                                    for t in range(len(announce_list[tier])):
 970                                        i = wxListItem()
 971                                        trackerList.InsertItem(i)
 972                                if announce is not None:
 973                                    for l in [1,2]:
 974                                        i = wxListItem()
 975                                        trackerList.InsertItem(i)
 976                
 977                                x = 0
 978                                for tier in range(len(announce_list)):
 979                                    for t in range(len(announce_list[tier])):
 980                                        if t == 0:
 981                                            trackerList.SetStringItem(x, 0, 'tier '+str(tier)+':')
 982                                        trackerList.SetStringItem(x, 1, announce_list[tier][t])
 983                                        x += 1
 984                                if announce is not None:
 985                                    trackerList.SetStringItem(x+1, 0, 'single:')
 986 theshadow 1.1                      trackerList.SetStringItem(x+1, 1, announce)
 987                                trackerList.SetColumnWidth(0,wxLIST_AUTOSIZE)
 988                                trackerList.SetColumnWidth(1,wxLIST_AUTOSIZE)
 989                                detailSizer.Add(trackerList)
 990                
 991                            if announce is None and announce_list is not None:
 992                                announce = announce_list[0][0]
 993                            if announce is not None:
 994                                detailSizer.Add(StaticText('likely tracker :'))
 995                                p = re.compile( '(.*/)[^/]+')
 996                                turl = p.sub (r'\1', announce)
 997 theshadow 1.13                 trackerUrl = StaticText(turl, self.FONT, True, 'Blue')
 998 theshadow 1.1                  detailSizer.Add(trackerUrl)
 999                            if metainfo.has_key('comment'):
1000                                detailSizer.Add(StaticText('comment :'))
1001                                detailSizer.Add(StaticText(metainfo['comment']))
1002                            if metainfo.has_key('creation date'):
1003                                detailSizer.Add(StaticText('creation date :'))
1004                                try:
1005                                    detailSizer.Add(StaticText(
1006                                        strftime('%x %X',localtime(metainfo['creation date']))))
1007                                except:
1008                                    try:
1009                                        detailSizer.Add(StaticText(metainfo['creation date']))
1010                                    except:
1011                                        detailSizer.Add(StaticText('<cannot read date>'))
1012                
1013                            detailSizer.AddGrowableCol(1)
1014                            colSizer.Add (detailSizer, 1, wxEXPAND)
1015                
1016                            okButton = wxButton(panel, -1, 'Ok')
1017                            colSizer.Add(okButton, 0, wxALIGN_RIGHT)
1018                            colSizer.AddGrowableCol(0)
1019 theshadow 1.1  
1020 theshadow 1.32             if not self.configfileargs['gui_stretchwindow']:
1021 theshadow 1.1                  aboutTitle.SetSize((400,-1))
1022                            else:
1023 theshadow 1.13                 panel.SetAutoLayout(True)
1024 theshadow 1.1  
1025                            border = wxBoxSizer(wxHORIZONTAL)
1026                            border.Add(colSizer, 1, wxEXPAND | wxALL, 4)
1027                            panel.SetSizer(border)
1028 theshadow 1.13             panel.SetAutoLayout(True)
1029 theshadow 1.1  
1030 theshadow 1.84             if fileselector and fileListID:
1031 theshadow 1.26                 def onRightClick(evt, self = self):
1032 theshadow 1.28                     s = []
1033                                    i = -1
1034                                    while True:
1035                                        i = self.fileList.GetNextItem(i,state=wxLIST_STATE_SELECTED)
1036                                        if i == -1:
1037                                            break
1038                                        s.append(i)
1039                                    if not s:   # just in case
1040                                        return
1041                                    oldstate = self.dow.fileselector[s[0]]
1042                                    kind=wxITEM_RADIO
1043                                    for i in s[1:]:
1044                                        if self.dow.fileselector[i] != oldstate:
1045                                            oldstate = None
1046                                            kind = wxITEM_NORMAL
1047                                            break
1048 theshadow 1.26                     menu = wxMenu()
1049 theshadow 1.28                     menu.Append(self.priorityIDs[1], "download first", kind=kind)
1050                                    menu.Append(self.priorityIDs[2], "download normally", kind=kind)
1051                                    menu.Append(self.priorityIDs[3], "download later", kind=kind)
1052 theshadow 1.84                     menu.Append(self.priorityIDs[0], "download never (deletes)", kind=kind)
1053 theshadow 1.28                     if oldstate is not None:
1054                                        menu.Check(self.priorityIDs[oldstate+1], True)
1055 theshadow 1.26 
1056                                    def onSelection(evt, self = self, s = s):
1057                                        p = evt.GetId()
1058 theshadow 1.28                         priorities = self.dow.fileselector.get_priorities()
1059 theshadow 1.26                         for i in xrange(len(self.priorityIDs)):
1060                                            if p == self.priorityIDs[i]:
1061 theshadow 1.28                                 for ss in s:
1062                                                    priorities[ss] = i-1
1063                                                    item = self.fileList.GetItem(ss)
1064                                                    item.SetTextColour(self.prioritycolors[i])
1065                                                    self.fileList.SetItem(item)
1066                                                self.dow.fileselector.set_priorities(priorities)
1067 theshadow 1.26                                 self.fileList.Refresh()
1068 theshadow 1.28                                 self.refresh_details = True
1069 theshadow 1.26                                 break
1070                                        
1071                                    for id in self.priorityIDs:
1072                                        EVT_MENU(self.detailBox, id, onSelection)
1073                
1074                                    self.detailBox.PopupMenu(menu, evt.GetPoint())
1075                                        
1076                                EVT_LIST_ITEM_RIGHT_CLICK(self.detailBox, fileListID, onRightClick)
1077                
1078                            def closeDetail(evt, self = self):
1079 theshadow 1.83                 if self.detailBox:
1080                                    self.detailBox.Close()
1081 theshadow 1.1              EVT_BUTTON(self.detailBox, okButton.GetId(), closeDetail)
1082 theshadow 1.26             def kill(evt, self = self):
1083 theshadow 1.80                 try:
1084                                    self.detailBox.RemoveIcon()
1085                                except:
1086                                    pass
1087 theshadow 1.26                 self.detailBox.Destroy()
1088                                self.detailBox = None
1089                                self.fileList = None
1090                                self.dow.filedatflag.clear()
1091 theshadow 1.1              EVT_CLOSE(self.detailBox, kill)
1092                
1093                            def trackerurl(self, turl = turl):
1094 theshadow 1.26                 try:
1095                                    Thread(target = open_new(turl)).start()
1096                                except:
1097                                    pass
1098 theshadow 1.1              EVT_LEFT_DOWN(trackerUrl, trackerurl)
1099                
1100                            self.detailBox.Show ()
1101                            border.Fit(panel)
1102                            self.detailBox.Fit()
1103                
1104 theshadow 1.13             self.refresh_details = True
1105 theshadow 1.1              self.dow.filedatflag.set()
1106                        except:
1107                            self.exception()
1108                
1109                
1110                    def credits(self, event):
1111                        try:
1112                            if (self.creditsBox is not None):
1113                                try:
1114 theshadow 1.80                     self.creditsBox.Close()
1115 theshadow 1.1                  except wxPyDeadObjectError, e:
1116                                    self.creditsBox = None
1117                
1118 theshadow 1.56             self.creditsBox = wxFrame(None, -1, 'Credits', size = (1,1),
1119                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
1120 theshadow 1.75             try:
1121 theshadow 1.1                  self.creditsBox.SetIcon(self.icon)
1122 theshadow 1.75             except:
1123                                pass
1124 theshadow 1.1  
1125                            panel = wxPanel(self.creditsBox, -1)        
1126                
1127 theshadow 1.13             def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
1128 theshadow 1.1                  x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT)
1129                                x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline))
1130                                if color is not None:
1131                                    x.SetForegroundColour(color)
1132                                return x
1133                
1134                            colSizer = wxFlexGridSizer(cols = 1, vgap = 3)
1135                
1136                            titleSizer = wxBoxSizer(wxHORIZONTAL)
1137                            aboutTitle = StaticText('Credits', self.FONT+4)
1138                            titleSizer.Add (aboutTitle)
1139                            colSizer.Add (titleSizer)
1140                            colSizer.Add (StaticText(
1141                              'The following people have all helped with this\n' +
1142                              'version of BitTorrent in some way (in no particular order) -\n'));
1143                            creditSizer = wxFlexGridSizer(cols = 3)
1144                            creditSizer.Add(StaticText(
1145                              'Bill Bumgarner\n' +
1146                              'David Creswick\n' +
1147                              'Andrew Loewenstern\n' +
1148                              'Ross Cohen\n' +
1149 theshadow 1.1                'Jeremy Avnet\n' +
1150                              'Greg Broiles\n' +
1151                              'Barry Cohen\n' +
1152                              'Bram Cohen\n' +
1153                              'sayke\n' +
1154                              'Steve Jenson\n' +
1155                              'Myers Carpenter\n' +
1156                              'Francis Crick\n' +
1157                              'Petru Paler\n' +
1158                              'Jeff Darcy\n' +
1159                              'John Gilmore\n' +
1160                              'Xavier Bassery\n' +
1161                              'Pav Lucistnik'))
1162                            creditSizer.Add(StaticText('  '))
1163                            creditSizer.Add(StaticText(
1164                              'Yann Vernier\n' +
1165                              'Pat Mahoney\n' +
1166                              'Boris Zbarsky\n' +
1167                              'Eric Tiedemann\n' +
1168                              'Henry "Pi" James\n' +
1169                              'Loring Holden\n' +
1170 theshadow 1.1                'Robert Stone\n' +
1171                              'Michael Janssen\n' +
1172                              'Eike Frost\n' +
1173                              'Andrew Todd\n' +
1174                              'otaku\n' +
1175                              'Edward Keyes\n' +
1176                              'John Hoffman\n' +
1177                              'Uoti Urpala\n' +
1178                              'Jon Wolf\n' +
1179 theshadow 1.66               'Christoph Hohmann\n' +
1180                              'Micah Anderson'))
1181 theshadow 1.1              colSizer.Add (creditSizer, flag = wxALIGN_CENTER_HORIZONTAL)
1182                            okButton = wxButton(panel, -1, 'Ok')
1183                            colSizer.Add(okButton, 0, wxALIGN_RIGHT)
1184                            colSizer.AddGrowableCol(0)
1185                
1186                            border = wxBoxSizer(wxHORIZONTAL)
1187                            border.Add(colSizer, 1, wxEXPAND | wxALL, 4)
1188                            panel.SetSizer(border)
1189 theshadow 1.13             panel.SetAutoLayout(True)
1190 theshadow 1.1  
1191 theshadow 1.83             def closeCredits(e, self = self):
1192                                if self.creditsBox:
1193                                    self.creditsBox.Close()
1194 theshadow 1.1              EVT_BUTTON(self.creditsBox, okButton.GetId(), closeCredits)
1195 theshadow 1.83             def kill(e, self = self):
1196 theshadow 1.80                 try:
1197 theshadow 1.83                     self.creditsBox.RemoveIcon()
1198 theshadow 1.80                 except:
1199                                    pass
1200 theshadow 1.83                 self.creditsBox.Destroy()
1201                                self.creditsBox = None
1202 theshadow 1.1              EVT_CLOSE(self.creditsBox, kill)
1203                
1204                            self.creditsBox.Show()
1205                            border.Fit(panel)
1206                            self.creditsBox.Fit()
1207                        except:
1208                            self.exception()
1209                
1210                
1211                    def statusIconHelp(self, event):
1212                        try:
1213                            if (self.statusIconHelpBox is not None):
1214                                try:
1215 theshadow 1.80                     self.statusIconHelpBox.Close()
1216 theshadow 1.1                  except wxPyDeadObjectError, e:
1217                                    self.statusIconHelpBox = None
1218                
1219 theshadow 1.56             self.statusIconHelpBox = wxFrame(None, -1, 'Help with the BitTorrent Status Light', size = (1,1),
1220                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
1221 theshadow 1.75             try:
1222 theshadow 1.1                  self.statusIconHelpBox.SetIcon(self.icon)
1223 theshadow 1.75             except:
1224                                pass
1225 theshadow 1.1  
1226                            panel = wxPanel(self.statusIconHelpBox, -1)
1227                
1228 theshadow 1.13             def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
1229 theshadow 1.1                  x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT)
1230                                x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline))
1231                                if color is not None:
1232                                    x.SetForegroundColour(color)
1233                                return x
1234                
1235                            fullsizer = wxFlexGridSizer(cols = 1, vgap = 13)
1236                            colsizer = wxFlexGridSizer(cols = 2, hgap = 13, vgap = 13)
1237                
1238                            disconnectedicon=self.createStatusIcon('disconnected')
1239                            colsizer.Add(wxStaticBitmap(panel, -1, disconnectedicon))
1240                            colsizer.Add(StaticText(
1241                                'Waiting to connect to the tracker.\n' +
1242                                'If the status light stays black for a long time the tracker\n' +
1243                                'you are trying to connect to may not be working.  Unless you\n' +
1244                                'are receiving a message telling you otherwise, please wait,\n' +
1245                                'and BitTorrent will automatically try to reconnect for you.'), 1, wxALIGN_CENTER_VERTICAL)
1246                
1247                            noconnectionsicon=self.createStatusIcon('noconnections')
1248                            colsizer.Add(wxStaticBitmap(panel, -1, noconnectionsicon))
1249                            colsizer.Add(StaticText(
1250 theshadow 1.1                  'You have no connections with other clients.\n' +
1251                                'Please be patient.  If after several minutes the status\n' +
1252                                'light remains red, this torrent may be old and abandoned.'), 1, wxALIGN_CENTER_VERTICAL)
1253                
1254                            noincomingicon=self.createStatusIcon('noincoming')
1255                            colsizer.Add(wxStaticBitmap(panel, -1, noincomingicon))
1256                            colsizer.Add(StaticText(
1257                                'You have not received any incoming connections from others.\n' +
1258                                'It may only be because no one has tried.  If you never see\n' +
1259                                'the status light turn green, it may indicate your system\n' +
1260                                'is behind a firewall or proxy server.  Please look into\n' +
1261                                'routing BitTorrent through your firewall in order to receive\n' +
1262                                'the best possible download rate.'), 1, wxALIGN_CENTER_VERTICAL)
1263                
1264                            nocompletesicon=self.createStatusIcon('nocompletes')
1265                            colsizer.Add(wxStaticBitmap(panel, -1, nocompletesicon))
1266                            colsizer.Add(StaticText(
1267                                'There are no complete copies among the clients you are\n' +
1268                                'connected to.  Don\'t panic, other clients in the torrent\n' +
1269                                "you can't see may have the missing data.\n" +
1270                                'If the status light remains blue, you may have problems\n' +
1271 theshadow 1.1                  'completing your download.'), 1, wxALIGN_CENTER_VERTICAL)
1272                
1273                            allgoodicon=self.createStatusIcon('allgood')
1274                            colsizer.Add(wxStaticBitmap(panel, -1, allgoodicon))
1275                            colsizer.Add(StaticText(
1276                                'The torrent is operating properly.'), 1, wxALIGN_CENTER_VERTICAL)
1277                
1278                            fullsizer.Add(colsizer, 0, wxALIGN_CENTER)
1279                            colsizer2 = wxFlexGridSizer(cols = 1, hgap = 13)
1280                
1281                            colsizer2.Add(StaticText(
1282                                'Please note that the status light is not omniscient, and that it may\n' +
1283                                'be wrong in many instances.  A torrent with a blue light may complete\n' +
1284                                "normally, and an occasional yellow light doesn't mean your computer\n" +
1285                                'has suddenly become firewalled.'), 1, wxALIGN_CENTER_VERTICAL)
1286                
1287                            colspacer = StaticText('  ')
1288                            colsizer2.Add(colspacer)
1289                
1290                            okButton = wxButton(panel, -1, 'Ok')
1291                            colsizer2.Add(okButton, 0, wxALIGN_CENTER)
1292 theshadow 1.1              fullsizer.Add(colsizer2, 0, wxALIGN_CENTER)
1293                
1294                            border = wxBoxSizer(wxHORIZONTAL)
1295                            border.Add(fullsizer, 1, wxEXPAND | wxALL, 4)
1296                
1297                            panel.SetSizer(border)
1298 theshadow 1.13             panel.SetAutoLayout(True)
1299 theshadow 1.1  
1300                
1301                            def closeHelp(self, frame = self):
1302 theshadow 1.80                 frame.statusIconHelpBox.Close()
1303 theshadow 1.1              EVT_BUTTON(self.statusIconHelpBox, okButton.GetId(), closeHelp)
1304                
1305                            self.statusIconHelpBox.Show ()
1306                            border.Fit(panel)
1307                            self.statusIconHelpBox.Fit()
1308                        except:
1309                            self.exception()
1310                
1311                
1312                    def openConfigMenu(self, event):
1313                        try:
1314                            self.configfile.configMenu(self)
1315                        except:
1316                            self.exception()
1317                
1318                
1319                    def advanced(self, event):
1320                        try:
1321 theshadow 1.52             if not self.dow or not self.filename:
1322 theshadow 1.1                  return
1323                            if (self.advBox is not None):
1324                                try:
1325                                    self.advBox.Close ()
1326                                except wxPyDeadObjectError, e:
1327                                    self.advBox = None
1328                
1329 theshadow 1.56             self.advBox = wxFrame(None, -1, 'BitTorrent Advanced', size = wxSize(200,200),
1330                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
1331 theshadow 1.75             try:
1332 theshadow 1.1                  self.advBox.SetIcon(self.icon)
1333 theshadow 1.75             except:
1334                                pass
1335 theshadow 1.1  
1336                            panel = wxPanel(self.advBox, -1, size = wxSize (200,200))
1337                
1338 theshadow 1.13             def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel):
1339 theshadow 1.1                  x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT)
1340                                x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline))
1341                                if color is not None:
1342                                    x.SetForegroundColour(color)
1343                                return x
1344                
1345                            colSizer = wxFlexGridSizer (cols = 1, vgap = 1)
1346                            colSizer.Add (StaticText('Advanced Info for ' + self.filename, self.FONT+4))
1347                
1348                            try:    # get system font width
1349                                fw = wxSystemSettings_GetFont(wxSYS_DEFAULT_GUI_FONT).GetPointSize()+1
1350                            except:
1351                                fw = wxSystemSettings_GetFont(wxSYS_SYSTEM_FONT).GetPointSize()+1
1352                
1353                            spewList = wxListCtrl(panel, -1, wxPoint(-1,-1), (fw*66,350), wxLC_REPORT|wxLC_HRULES|wxLC_VRULES)
1354                            self.spewList = spewList
1355 theshadow 1.13             spewList.SetAutoLayout (True)
1356 theshadow 1.1  
1357                            colSizer.Add(spewList, -1, wxEXPAND)
1358                
1359                            colSizer.Add(StaticText(''))
1360                            self.storagestats1 = StaticText('')
1361                            self.storagestats2 = StaticText('')
1362                            colSizer.Add(self.storagestats1, -1, wxEXPAND)
1363                            colSizer.Add(self.storagestats2, -1, wxEXPAND)
1364 theshadow 1.40             spinnerSizer = wxFlexGridSizer(cols=4,vgap=0,hgap=0)
1365 theshadow 1.36             cstats = '          Listening on '
1366                            if self.connection_stats['interfaces']:
1367                                cstats += ', '.join(self.connection_stats['interfaces']) + ' on '
1368                            cstats += 'port ' + str(self.connection_stats['port'])
1369                            if self.connection_stats['upnp']:
1370                                cstats += ', UPnP port forwarded'
1371 theshadow 1.40             spinnerSizer.Add(StaticText(cstats), -1, wxEXPAND)
1372                            spinnerSizer.AddGrowableCol(0)
1373                            spinnerSizer.Add(StaticText('Max download rate (kB/s) '),0,wxALIGN_CENTER_VERTICAL)
1374                            self.downrateSpinner = wxSpinCtrl (panel, -1, "", (-1,-1), (50, -1))
1375                            self.downrateSpinner.SetFont(self.default_font)
1376                            self.downrateSpinner.SetRange(0,5000)
1377                            self.downrateSpinner.SetValue(self.config['max_download_rate'])
1378                            spinnerSizer.Add (self.downrateSpinner, 0)
1379                            EVT_SPINCTRL(self.downrateSpinner, -1, self.onDownRateSpinner)
1380                            spinnerSizer.Add(StaticText(' (0 = unlimited)  '),0,wxALIGN_CENTER_VERTICAL)
1381                            colSizer.Add(spinnerSizer,0,wxEXPAND)
1382                
1383 theshadow 1.1              colSizer.Add(StaticText(''))
1384                
1385                            buttonSizer = wxFlexGridSizer (cols = 5, hgap = 20)
1386                
1387                            reannounceButton = wxButton(panel, -1, 'Manual Announce')
1388                            buttonSizer.Add (reannounceButton)
1389                
1390                            extannounceButton = wxButton(panel, -1, 'External Announce')
1391                            buttonSizer.Add (extannounceButton)
1392                
1393                            bgallocButton = wxButton(panel, -1, 'Finish Allocation')
1394                            buttonSizer.Add (bgallocButton)
1395                
1396                            buttonSizer.Add(StaticText(''))
1397                
1398                            okButton = wxButton(panel, -1, 'Ok')
1399                            buttonSizer.Add (okButton)
1400                
1401                            colSizer.Add (buttonSizer, 0, wxALIGN_CENTER)
1402                            colSizer.AddGrowableCol(0)
1403                            colSizer.AddGrowableRow(1)
1404 theshadow 1.1  
1405                            panel.SetSizer(colSizer)
1406 theshadow 1.13             panel.SetAutoLayout(True)
1407 theshadow 1.1  
1408                            spewList.InsertColumn(0, "Optimistic Unchoke", format=wxLIST_FORMAT_CENTER, width=fw*2)
1409                            spewList.InsertColumn(1, "Peer ID", width=0)
1410                            spewList.InsertColumn(2, "IP", width=fw*11)
1411                            spewList.InsertColumn(3, "Local/Remote", format=wxLIST_FORMAT_CENTER, width=fw*3)
1412                            spewList.InsertColumn(4, "Up", format=wxLIST_FORMAT_RIGHT, width=fw*6)
1413                            spewList.InsertColumn(5, "Interested", format=wxLIST_FORMAT_CENTER, width=fw*2)
1414                            spewList.InsertColumn(6, "Choking", format=wxLIST_FORMAT_CENTER, width=fw*2)
1415                            spewList.InsertColumn(7, "Down", format=wxLIST_FORMAT_RIGHT, width=fw*6)
1416                            spewList.InsertColumn(8, "Interesting", format=wxLIST_FORMAT_CENTER, width=fw*2)
1417                            spewList.InsertColumn(9, "Choked", format=wxLIST_FORMAT_CENTER, width=fw*2)
1418                            spewList.InsertColumn(10, "Snubbed", format=wxLIST_FORMAT_CENTER, width=fw*2)
1419                            spewList.InsertColumn(11, "Downloaded", format=wxLIST_FORMAT_RIGHT, width=fw*7)
1420                            spewList.InsertColumn(12, "Uploaded", format=wxLIST_FORMAT_RIGHT, width=fw*7)
1421                            spewList.InsertColumn(13, "Completed", format=wxLIST_FORMAT_RIGHT, width=fw*6)
1422                            spewList.InsertColumn(14, "Peer Download Speed", format=wxLIST_FORMAT_RIGHT, width=fw*6)
1423                
1424                            def reannounce(self, frame = self):
1425 theshadow 1.37                 if (clock() - frame.reannouncelast > 60):
1426                                    frame.reannouncelast = clock()
1427 theshadow 1.1                      frame.dow.reannounce()
1428                            EVT_BUTTON(self.advBox, reannounceButton.GetId(), reannounce)
1429                
1430                            self.advextannouncebox = None
1431                            def reannounce_external(self, frame = self):
1432                                if (frame.advextannouncebox is not None):
1433                                    try:
1434                                        frame.advextannouncebox.Close ()
1435                                    except wxPyDeadObjectError, e:
1436                                        frame.advextannouncebox = None
1437                
1438 theshadow 1.56                 frame.advextannouncebox = wxFrame(None, -1, 'External Announce', size = (1,1),
1439                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
1440 theshadow 1.75                 try:
1441 theshadow 1.1                      frame.advextannouncebox.SetIcon(frame.icon)
1442 theshadow 1.75                 except:
1443                                    pass
1444 theshadow 1.1  
1445                                panel = wxPanel(frame.advextannouncebox, -1)
1446                
1447                                fullsizer = wxFlexGridSizer(cols = 1, vgap = 13)
1448                                msg = wxStaticText(panel, -1, "Enter tracker anounce URL:")
1449                                msg.SetFont(frame.default_font)
1450                                fullsizer.Add(msg)
1451                
1452                                frame.advexturl = wxTextCtrl(parent = panel, id = -1, value = '',
1453                                                    size = (255, 20), style = wxTE_PROCESS_TAB)
1454                                frame.advexturl.SetFont(frame.default_font)
1455                                frame.advexturl.SetValue(frame.lastexternalannounce)
1456                                fullsizer.Add(frame.advexturl)
1457                
1458                                buttonSizer = wxFlexGridSizer (cols = 2, hgap = 10)
1459                
1460                                okButton = wxButton(panel, -1, 'OK')
1461                                buttonSizer.Add (okButton)
1462                
1463                                cancelButton = wxButton(panel, -1, 'Cancel')
1464                                buttonSizer.Add (cancelButton)
1465 theshadow 1.1  
1466                                fullsizer.Add (buttonSizer, 0, wxALIGN_CENTER)
1467                
1468                                border = wxBoxSizer(wxHORIZONTAL)
1469                                border.Add(fullsizer, 1, wxEXPAND | wxALL, 4)
1470                
1471                                panel.SetSizer(border)
1472 theshadow 1.13                 panel.SetAutoLayout(True)
1473 theshadow 1.1  
1474                                def ok(self, frame = frame):
1475                                    special = frame.advexturl.GetValue()
1476                                    if special:
1477                                        frame.lastexternalannounce = special
1478 theshadow 1.37                         if (clock() - frame.reannouncelast > 60):
1479                                            frame.reannouncelast = clock()
1480 theshadow 1.1                              frame.dow.reannounce(special)
1481                                    frame.advextannouncebox.Close()
1482                                EVT_BUTTON(frame.advextannouncebox, okButton.GetId(), ok)
1483                
1484                                def cancel(self, frame = frame):
1485                                    frame.advextannouncebox.Close()
1486                                EVT_BUTTON(frame.advextannouncebox, cancelButton.GetId(), cancel)
1487                
1488                                frame.advextannouncebox.Show ()
1489                                fullsizer.Fit(panel)
1490                                frame.advextannouncebox.Fit()
1491                
1492                            EVT_BUTTON(self.advBox, extannounceButton.GetId(), reannounce_external)
1493                
1494                            def bgalloc(self, frame = self):
1495                                if frame.dow.storagewrapper is not None:
1496                                    frame.dow.storagewrapper.bgalloc()
1497                            EVT_BUTTON(self.advBox, bgallocButton.GetId(), bgalloc)
1498                
1499 theshadow 1.48             def closeAdv(evt, self = self):
1500 theshadow 1.80                 self.advBox.Close()
1501 theshadow 1.48             def killAdv(evt, self = self):
1502 theshadow 1.80                 try:
1503                                    self.advBox.RemoveIcon()
1504                                except:
1505                                    pass
1506 theshadow 1.48                 self.onDownRateSpinner()
1507                                self.dow.spewflag.clear()
1508                                self.advBox.Destroy()
1509                                self.advBox = None
1510                                if (self.advextannouncebox is not None):
1511 theshadow 1.1                      try:
1512 theshadow 1.80                         self.advextannouncebox.Close()
1513 theshadow 1.1                      except wxPyDeadObjectError, e:
1514                                        pass
1515 theshadow 1.48                     self.advextannouncebox = None
1516 theshadow 1.1              EVT_BUTTON(self.advBox, okButton.GetId(), closeAdv)
1517                            EVT_CLOSE(self.advBox, killAdv)
1518                
1519                            self.advBox.Show ()
1520                            colSizer.Fit(panel)
1521                            self.advBox.Fit()
1522 theshadow 1.17             if self.dow:
1523                                self.dow.spewflag.set()
1524 theshadow 1.1          except:
1525                            self.exception()
1526                
1527                
1528                    def displayUsage(self, text):
1529 theshadow 1.8          self.invokeLater(self.onDisplayUsage, [text])
1530                
1531                    def onDisplayUsage(self, text):        
1532 theshadow 1.1          try:
1533                            self.done(None)
1534 theshadow 1.56             w = wxFrame(None, -1, 'BITTORRENT USAGE',
1535                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
1536 theshadow 1.8              panel = wxPanel(w, -1)
1537                            sizer = wxFlexGridSizer(cols = 1)
1538                            sizer.Add(wxTextCtrl(panel, -1, text,
1539                                        size = (500,300), style = wxTE_READONLY|wxTE_MULTILINE))
1540 theshadow 1.1              okButton = wxButton(panel, -1, 'Ok')
1541                
1542                            def closeUsage(self, frame = self):
1543                                frame.usageBox.Close()
1544 theshadow 1.8              EVT_BUTTON(w, okButton.GetId(), closeUsage)
1545 theshadow 1.1              def kill(self, frame = self):
1546                                frame.usageBox.Destroy()
1547                                frame.usageBox = None
1548 theshadow 1.8              EVT_CLOSE(w, kill)
1549 theshadow 1.1  
1550 theshadow 1.8              sizer.Add(okButton, 0, wxALIGN_RIGHT)
1551                            border = wxBoxSizer(wxHORIZONTAL)
1552                            border.Add(sizer, 1, wxEXPAND | wxALL, 4)
1553                
1554                            panel.SetSizer(border)
1555 theshadow 1.13             panel.SetAutoLayout(True)
1556 theshadow 1.1  
1557 theshadow 1.8              border.Fit(panel)
1558                            w.Fit()
1559                            w.Show()
1560                            self.usageBox = w
1561 theshadow 1.1          except:
1562                            self.exception()
1563                
1564                
1565 theshadow 1.70     def updateStatus(self, dpflag = Event(), fractionDone = None,
1566 theshadow 1.1              timeEst = None, downRate = None, upRate = None,
1567                            activity = None, statistics = None, spew = None, sizeDone = None,
1568                            **kws):
1569                        if activity is not None:
1570                            self.activity = activity
1571 theshadow 1.47         self.gui_fractiondone = fractionDone
1572 theshadow 1.75         self.invokeLater(self.onUpdateStatus,
1573 theshadow 1.70                  [dpflag, timeEst, downRate, upRate, statistics, spew, sizeDone])
1574 theshadow 1.1  
1575 theshadow 1.70     def onUpdateStatus(self, dpflag, timeEst, downRate, upRate,
1576                                             statistics, spew, sizeDone):
1577 theshadow 1.1          if self.firstupdate:
1578 theshadow 1.41             if not self.old_ratesettings:
1579                                self.old_ratesettings = {}
1580                            self.connChoice.SetStringSelection(
1581                                self.old_ratesettings.get('rate setting',
1582                                                  self.configfileargs['gui_ratesettingsdefault']))
1583                            self.onConnChoice(0,
1584                                              self.old_ratesettings.get('uploads'),
1585                                              self.old_ratesettings.get('max upload rate'))
1586                            if self.old_ratesettings.has_key('max download rate'):
1587                                self.dow.setDownloadRate(self.old_ratesettings['max download rate'])
1588                                if self.advBox:
1589                                    self.downrateSpinner.SetValue(self.old_ratesettings['max download rate'])
1590 theshadow 1.13             self.firstupdate = False
1591 theshadow 1.17             if self.advBox:
1592                                self.dow.spewflag.set()
1593 theshadow 1.75         if self.ispaused or statistics is None:
1594 theshadow 1.1              self.setStatusIcon('startup')
1595                        elif statistics.numPeers + statistics.numSeeds + statistics.numOldSeeds == 0:
1596                            if statistics.last_failed:
1597                                self.setStatusIcon('disconnected')
1598                            else:
1599                                self.setStatusIcon('noconnections')
1600                        elif ( not statistics.external_connection_made
1601 theshadow 1.32             and not self.configfileargs['gui_forcegreenonfirewall'] ):
1602 theshadow 1.1              self.setStatusIcon('noincoming')
1603                        elif ( (statistics.numSeeds + statistics.numOldSeeds == 0)
1604                               and ( (self.fin and statistics.numCopies < 1)
1605                                    or (not self.fin and statistics.numCopies2 < 1) ) ):
1606                            self.setStatusIcon('nocompletes')
1607 theshadow 1.47         elif timeEst == 0 and sizeDone < self.torrentsize:
1608                            self.setStatusIcon('nocompletes')
1609 theshadow 1.1          else:
1610                            self.setStatusIcon('allgood')
1611 theshadow 1.47         if statistics is None:
1612                            self.setgaugemode(-1)
1613                        elif self.gui_fractiondone == None or self.gui_fractiondone == 1.0:
1614                            self.setgaugemode(1)
1615                        else:
1616                            self.setgaugemode(0)
1617 theshadow 1.1  
1618                        if self.updateSliderFlag == 1:
1619                            self.updateSliderFlag = 0
1620                            newValue = (self.rateSpinner.GetValue()
1621                                         / connChoices[self.connChoice.GetSelection()]['rate'].get('div',1))
1622                            if self.rateslider.GetValue() != newValue:
1623                                self.rateslider.SetValue(newValue)
1624                        if self.updateSpinnerFlag == 1:
1625                            self.updateSpinnerFlag = 0
1626                            cc = connChoices[self.connChoice.GetSelection()]
1627                            if cc.has_key('rate'):
1628                                newValue = (self.rateslider.GetValue() * cc['rate'].get('div',1))
1629                                if self.rateSpinner.GetValue() != newValue:
1630                                    self.rateSpinner.SetValue(newValue)
1631 theshadow 1.63 
1632 theshadow 1.1          if self.fin:
1633                            if statistics is not None:
1634                                if statistics.numOldSeeds > 0 or statistics.numCopies > 1:
1635                                    self.gauge.SetValue(1000)
1636                                else:
1637                                    self.gauge.SetValue(int(1000*statistics.numCopies))
1638                        elif self.gui_fractiondone is not None:
1639                            gaugelevel = int(self.gui_fractiondone * 1000)
1640                            self.gauge.SetValue(gaugelevel)
1641                            if statistics is not None and statistics.downTotal is not None:
1642 theshadow 1.32                 if self.configfileargs['gui_displaymiscstats']:
1643 theshadow 1.1                      self.frame.SetTitle('%.1f%% (%.2f MiB) %s - BitTorrent %s' % (float(gaugelevel)/10, float(sizeDone) / (1<<20), self.filename, version))
1644                                else:
1645                                    self.frame.SetTitle('%.1f%% %s - BitTorrent %s' % (float(gaugelevel)/10, self.filename, version))
1646                            else:
1647                                self.frame.SetTitle('%.0f%% %s - BitTorrent %s' % (float(gaugelevel)/10, self.filename, version))
1648 theshadow 1.79         if self.ispaused:
1649                            self.timeText.SetLabel(hours(clock() - self.starttime) + ' /')
1650                        elif timeEst is None:
1651                            self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' + self.activity)
1652                        else:
1653 theshadow 1.37             self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' + hours(timeEst))
1654 theshadow 1.75         if not self.ispaused:
1655                            if downRate is not None:
1656                                self.downRateText.SetLabel('%.0f kB/s' % (float(downRate) / 1000))
1657                            if upRate is not None:
1658                                self.upRateText.SetLabel('%.0f kB/s' % (float(upRate) / 1000))
1659 theshadow 1.23         if self.taskbaricon:
1660 theshadow 1.1              icontext='BitTorrent '
1661                            if self.gui_fractiondone is not None and not self.fin:
1662                                if statistics is not None and statistics.downTotal is not None:
1663                                    icontext=icontext+' %.1f%% (%.2f MiB)' % (self.gui_fractiondone*100, float(sizeDone) / (1<<20))
1664                                else:
1665                                    icontext=icontext+' %.0f%%' % (self.gui_fractiondone*100)
1666                            if upRate is not None:
1667                                icontext=icontext+' u:%.0f kB/s' % (float(upRate) / 1000)
1668                            if downRate is not None:
1669                                icontext=icontext+' d:%.0f kB/s' % (float(downRate) / 1000)
1670                            icontext+=' %s' % self.filename
1671 theshadow 1.25             try:
1672 theshadow 1.82                 if self.fin:
1673                                    self.frame.tbicon.SetIcon(self.finicon,icontext)
1674                                else:
1675                                    self.frame.tbicon.SetIcon(self.icon,icontext)
1676 theshadow 1.25             except:
1677                                pass
1678 theshadow 1.1          if statistics is not None:
1679 theshadow 1.63             if self.autorate:
1680                                self.rateSpinner.SetValue(statistics.upRate)
1681                                self.connSpinner.SetValue(statistics.upSlots)
1682                
1683 theshadow 1.41             downtotal = statistics.downTotal + self.old_download
1684                            uptotal = statistics.upTotal + self.old_upload
1685 theshadow 1.32             if self.configfileargs['gui_displaymiscstats']:
1686 theshadow 1.41                 self.downText.SetLabel('%.2f MiB' % (float(downtotal) / (1 << 20)))
1687                                self.upText.SetLabel('%.2f MiB' % (float(uptotal) / (1 << 20)))
1688                            if downtotal > 0:
1689                                sharerating = float(uptotal)/downtotal
1690                                if sharerating == 0:
1691                                    shareSmiley = ''
1692                                    color = 'Black'
1693                                elif sharerating < 0.5:
1694 theshadow 1.1                      shareSmiley = ':-('
1695                                    color = 'Red'
1696 theshadow 1.41                 elif sharerating < 1.0:
1697                                    shareSmiley = ':-|'
1698                                    color = 'Orange'
1699 theshadow 1.1                  else:
1700 theshadow 1.41                     shareSmiley = ':-)'
1701                                    color = 'Forest Green'
1702                            elif uptotal == 0:
1703                                sharerating = None
1704                                shareSmiley = ''
1705                                color = 'Black'
1706                            else:
1707                                sharerating = None
1708                                shareSmiley = '00 :-D'
1709                                color = 'Forest Green'
1710                            if sharerating is None:
1711                                self.shareRatingText.SetLabel(shareSmiley)
1712                            else:
1713                                self.shareRatingText.SetLabel('%.3f %s' % (sharerating, shareSmiley))
1714                            self.shareRatingText.SetForegroundColour(color)
1715 theshadow 1.1  
1716 theshadow 1.32             if self.configfileargs['gui_displaystats']:
1717 theshadow 1.1                  if not self.fin:
1718                                    self.seedStatusText.SetLabel('connected to %d seeds; also seeing %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies2)))
1719                                else:
1720                                    self.seedStatusText.SetLabel('%d seeds seen recently; also seeing %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies)))
1721                                self.peerStatusText.SetLabel('connected to %d peers with an average of %.1f%% completed (total speed %.0f kB/s)' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1000)))
1722 theshadow 1.37         if ((clock() - self.lastError) > 300):
1723 theshadow 1.1              self.errorText.SetLabel('')
1724                
1725 theshadow 1.32         if ( self.configfileargs['gui_displaymiscstats']
1726 theshadow 1.1              and statistics is not None and statistics.backgroundallocating ):
1727                            self.bgalloc_periods += 1
1728                            if self.bgalloc_periods > 3:
1729                                self.bgalloc_periods = 0
1730                            self.bgallocText.SetLabel('ALLOCATING'+(' .'*self.bgalloc_periods))
1731                        elif self.dow.superseedflag.isSet():
1732                            self.bgallocText.SetLabel('SUPER-SEED')
1733                        else:
1734                            self.bgallocText.SetLabel('')
1735                
1736                
1737 theshadow 1.37         if spew is not None and (clock()-self.spewwait>1):
1738 theshadow 1.1              if (self.advBox is not None):
1739 theshadow 1.37                 self.spewwait = clock()
1740 theshadow 1.1                  spewList = self.spewList
1741                                spewlen = len(spew)+2
1742                                if statistics is not None:
1743                                    kickbanlen = len(statistics.peers_kicked)+len(statistics.peers_banned)
1744                                    if kickbanlen:
1745                                        spewlen += kickbanlen+1
1746                                else:
1747                                    kickbanlen = 0
1748                                for x in range(spewlen-spewList.GetItemCount()):
1749                                    i = wxListItem()
1750                                    spewList.InsertItem(i)
1751                                for x in range(spewlen,spewList.GetItemCount()):
1752                                    spewList.DeleteItem(len(spew)+1)
1753                
1754                                tot_uprate = 0.0
1755                                tot_downrate = 0.0
1756                                for x in range(len(spew)):
1757                                    if (spew[x]['optimistic'] == 1):
1758                                        a = '*'
1759                                    else:
1760                                        a = ' '
1761 theshadow 1.1                      spewList.SetStringItem(x, 0, a)
1762 theshadow 1.14                     spewList.SetStringItem(x, 1, spew[x]['id'])
1763 theshadow 1.1                      spewList.SetStringItem(x, 2, spew[x]['ip'])
1764                                    spewList.SetStringItem(x, 3, spew[x]['direction'])
1765                                    if spew[x]['uprate'] > 100:
1766                                        spewList.SetStringItem(x, 4, '%.0f kB/s' % (float(spew[x]['uprate']) / 1000))
1767                                    else:
1768                                        spewList.SetStringItem(x, 4, ' ')
1769                                    tot_uprate += spew[x]['uprate']
1770                                    if (spew[x]['uinterested'] == 1):
1771                                        a = '*'
1772                                    else:
1773                                        a = ' '
1774                                    spewList.SetStringItem(x, 5, a)
1775                                    if (spew[x]['uchoked'] == 1):
1776                                        a = '*'
1777                                    else:
1778                                        a = ' '
1779                                    spewList.SetStringItem(x, 6, a)
1780                
1781                                    if spew[x]['downrate'] > 100:
1782                                        spewList.SetStringItem(x, 7, '%.0f kB/s' % (float(spew[x]['downrate']) / 1000))
1783                                    else:
1784 theshadow 1.1                          spewList.SetStringItem(x, 7, ' ')
1785                                    tot_downrate += spew[x]['downrate']
1786                
1787                                    if (spew[x]['dinterested'] == 1):
1788                                        a = '*'
1789                                    else:
1790                                        a = ' '
1791                                    spewList.SetStringItem(x, 8, a)
1792                                    if (spew[x]['dchoked'] == 1):
1793                                        a = '*'
1794                                    else:
1795                                        a = ' '
1796                                    spewList.SetStringItem(x, 9, a)
1797                                    if (spew[x]['snubbed'] == 1):
1798                                        a = '*'
1799                                    else:
1800                                        a = ' '
1801                                    spewList.SetStringItem(x, 10, a)
1802                                    spewList.SetStringItem(x, 11, '%.2f MiB' % (float(spew[x]['dtotal']) / (1 << 20)))
1803                                    if spew[x]['utotal'] is not None:
1804                                        a = '%.2f MiB' % (float(spew[x]['utotal']) / (1 << 20))
1805 theshadow 1.1                      else:
1806                                        a = ''
1807                                    spewList.SetStringItem(x, 12, a)
1808                                    spewList.SetStringItem(x, 13, '%.1f%%' % (float(int(spew[x]['completed']*1000))/10))
1809                                    if spew[x]['speed'] is not None:
1810                                        a = '%.0f kB/s' % (float(spew[x]['speed']) / 1000)
1811                                    else:
1812                                        a = ''
1813                                    spewList.SetStringItem(x, 14, a)
1814                
1815                                x = len(spew)
1816                                for i in range(15):
1817                                    spewList.SetStringItem(x, i, '')
1818                
1819                                x += 1
1820                                spewList.SetStringItem(x, 2, '         TOTALS:')
1821                                spewList.SetStringItem(x, 4, '%.0f kB/s' % (float(tot_uprate) / 1000))
1822                                spewList.SetStringItem(x, 7, '%.0f kB/s' % (float(tot_downrate) / 1000))
1823                                if statistics is not None:
1824                                    spewList.SetStringItem(x, 11, '%.2f MiB' % (float(statistics.downTotal) / (1 << 20)))
1825                                    spewList.SetStringItem(x, 12, '%.2f MiB' % (float(statistics.upTotal) / (1 << 20)))
1826 theshadow 1.1                  else:
1827                                    spewList.SetStringItem(x, 11, '')
1828                                    spewList.SetStringItem(x, 12, '')
1829                                for i in [0,1,3,5,6,8,9,10,13,14]:
1830                                    spewList.SetStringItem(x, i, '')
1831                
1832                                if kickbanlen:
1833                                    x += 1
1834                                    for i in range(14):
1835                                        spewList.SetStringItem(x, i, '')
1836                
1837                                    for peer in statistics.peers_kicked:
1838                                        x += 1
1839                                        spewList.SetStringItem(x, 2, peer[0])
1840 theshadow 1.14                         spewList.SetStringItem(x, 1, peer[1])
1841 theshadow 1.1                          spewList.SetStringItem(x, 4, 'KICKED')
1842                                        for i in [0,3,5,6,7,8,9,10,11,12,13,14]:
1843                                            spewList.SetStringItem(x, i, '')
1844                
1845                                    for peer in statistics.peers_banned:
1846                                        x += 1
1847                                        spewList.SetStringItem(x, 2, peer[0])
1848 theshadow 1.14                         spewList.SetStringItem(x, 1, peer[1])
1849 theshadow 1.1                          spewList.SetStringItem(x, 4, 'BANNED')
1850                                        for i in [0,3,5,6,7,8,9,10,11,12,13,14]:
1851                                            spewList.SetStringItem(x, i, '')
1852                
1853                                if statistics is not None:
1854 theshadow 1.36                     l1 = (
1855 theshadow 1.1                          '          currently downloading %d pieces (%d just started), %d pieces partially retrieved'
1856                                                        % ( statistics.storage_active,
1857                                                            statistics.storage_new,
1858                                                            statistics.storage_dirty ) )
1859 theshadow 1.36                     if statistics.storage_isendgame:
1860                                        l1 += ', endgame mode'
1861                                    self.storagestats2.SetLabel(l1)
1862                                    self.storagestats1.SetLabel(
1863                                        '          %d of %d pieces complete (%d just downloaded), %d failed hash check, %sKiB redundant data discarded'
1864 theshadow 1.1                                          % ( statistics.storage_numcomplete,
1865                                                            statistics.storage_totalpieces,
1866                                                            statistics.storage_justdownloaded,
1867 theshadow 1.21                                             statistics.storage_numflunked,
1868 theshadow 1.36                                             comma_format(int(statistics.discarded/1024)) ) )
1869 theshadow 1.1  
1870                        if ( self.fileList is not None and statistics is not None
1871 theshadow 1.70                 and (statistics.filelistupdated.isSet() or self.refresh_details) ):
1872 theshadow 1.1              for i in range(len(statistics.filecomplete)):
1873 theshadow 1.43                 if self.dow.fileselector[i] == -1:
1874                                    self.fileList.SetItemImage(i,0,0)
1875                                    self.fileList.SetStringItem(i,1,'')
1876                                elif statistics.fileinplace[i]:
1877 theshadow 1.1                      self.fileList.SetItemImage(i,2,2)
1878                                    self.fileList.SetStringItem(i,1,"done")
1879                                elif statistics.filecomplete[i]:
1880                                    self.fileList.SetItemImage(i,1,1)
1881                                    self.fileList.SetStringItem(i,1,"100%")
1882                                else:
1883 theshadow 1.28                     self.fileList.SetItemImage(i,0,0)
1884 theshadow 1.70                     frac = statistics.fileamtdone[i]
1885 theshadow 1.28                     if frac:
1886 theshadow 1.55                         self.fileList.SetStringItem(i,1,'%d%%' % (frac*100))
1887 theshadow 1.28                     else:
1888                                        self.fileList.SetStringItem(i,1,'')
1889 theshadow 1.1  
1890 theshadow 1.70             statistics.filelistupdated.clear()
1891                            self.refresh_details = False
1892                
1893 theshadow 1.32         if self.configfile.configReset():     # whoopee!  Set everything invisible! :-)
1894 theshadow 1.1  
1895 theshadow 1.32             self.dow.config['security'] = self.configfileargs['security']
1896 theshadow 1.1  
1897 theshadow 1.32             statsdisplayflag = self.configfileargs['gui_displaymiscstats']
1898 theshadow 1.1              self.downTextLabel.Show(statsdisplayflag)
1899                            self.upTextLabel.Show(statsdisplayflag)
1900                            self.fileDestLabel.Show(statsdisplayflag)
1901                            self.fileDestText.Show(statsdisplayflag)
1902                            self.colSizer.Layout()
1903                
1904                            self.downText.SetLabel('')          # blank these to flush them
1905                            self.upText.SetLabel('')
1906                            self.seedStatusText.SetLabel('')
1907                            self.peerStatusText.SetLabel('')
1908                
1909 theshadow 1.32             ratesettingsmode = self.configfileargs['gui_ratesettingsmode']
1910 theshadow 1.13             ratesettingsflag1 = True    #\ settings
1911                            ratesettingsflag2 = False   #/ for 'basic'
1912 theshadow 1.1              if ratesettingsmode == 'none':
1913 theshadow 1.13                 ratesettingsflag1 = False
1914 theshadow 1.1              elif ratesettingsmode == 'full':
1915 theshadow 1.13                 ratesettingsflag2 = True
1916 theshadow 1.1              self.connChoiceLabel.Show(ratesettingsflag1)
1917                            self.connChoice.Show(ratesettingsflag1)
1918                            self.rateSpinnerLabel.Show(ratesettingsflag2)
1919                            self.rateSpinner.Show(ratesettingsflag2)
1920                            self.rateLowerText.Show(ratesettingsflag2)
1921                            self.rateUpperText.Show(ratesettingsflag2)
1922                            self.rateslider.Show(ratesettingsflag2)
1923                            self.connSpinnerLabel.Show(ratesettingsflag2)
1924                            self.connSpinner.Show(ratesettingsflag2)
1925                            self.connLowerText.Show(ratesettingsflag2)
1926                            self.connUpperText.Show(ratesettingsflag2)
1927                            self.connslider.Show(ratesettingsflag2)
1928                            self.unlimitedLabel.Show(ratesettingsflag2)
1929                
1930 theshadow 1.47             self.setgaugemode(None)
1931 theshadow 1.1  
1932                        self.frame.Layout()
1933                        self.frame.Refresh()
1934                
1935                        self.gui_fractiondone = None
1936 theshadow 1.70         dpflag.set()
1937 theshadow 1.1  
1938                
1939                    def finished(self):
1940 theshadow 1.13         self.fin = True
1941 theshadow 1.1          self.invokeLater(self.onFinishEvent)
1942                
1943                    def failed(self):
1944 theshadow 1.13         self.fin = True
1945 theshadow 1.1          self.invokeLater(self.onFailEvent)
1946                
1947                    def error(self, errormsg):
1948                        self.invokeLater(self.onErrorEvent, [errormsg])
1949                
1950                    def onFinishEvent(self):
1951 theshadow 1.37         self.activity = hours(clock() - self.starttime) + ' / ' +'Download Succeeded!'
1952 theshadow 1.1          self.cancelButton.SetLabel('Finish')
1953                        self.gauge.SetValue(0)
1954                        self.frame.SetTitle('%s - Upload - BitTorrent %s' % (self.filename, version))
1955 theshadow 1.75         try:
1956 theshadow 1.80             self.frame.SetIcon(self.finicon)
1957 theshadow 1.75         except:
1958                            pass
1959 theshadow 1.23         if self.taskbaricon:
1960 theshadow 1.80             self.frame.tbicon.SetIcon(self.finicon, "BitTorrent - Finished")
1961 theshadow 1.1          self.downRateText.SetLabel('')
1962                
1963                    def onFailEvent(self):
1964                        if not self.shuttingdown:
1965 theshadow 1.37             self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' +'Failed!')
1966 theshadow 1.1              self.activity = 'Failed!'
1967                            self.cancelButton.SetLabel('Close')
1968                            self.gauge.SetValue(0)
1969                            self.downRateText.SetLabel('')
1970                            self.setStatusIcon('startup')
1971                
1972                    def onErrorEvent(self, errormsg):
1973 theshadow 1.7          if errormsg[:2] == '  ':    # indent at least 2 spaces means a warning message
1974                            self.errorText.SetLabel(errormsg)
1975 theshadow 1.37             self.lastError = clock()
1976 theshadow 1.7          else:
1977 theshadow 1.78             self.errorText.SetLabel(strftime('ERROR (%x %X) -\n') + errormsg)
1978 theshadow 1.37             self.lastError = clock()
1979 theshadow 1.1  
1980                
1981                    def chooseFile(self, default, size, saveas, dir):
1982                        f = Event()
1983                        bucket = [None]
1984                        self.invokeLater(self.onChooseFile, [default, bucket, f, size, dir, saveas])
1985                        f.wait()
1986                        return bucket[0]
1987                
1988                    def onChooseFile(self, default, bucket, f, size, dir, saveas):
1989                        if saveas == '':
1990 theshadow 1.32             if self.configfileargs['gui_default_savedir'] != '':
1991                                start_dir = self.configfileargs['gui_default_savedir']
1992 theshadow 1.1              else:
1993 theshadow 1.32                 start_dir = self.configfileargs['last_saved']
1994 theshadow 1.1              if not isdir(start_dir):    # if it's not set properly
1995                                start_dir = '/'    # yes, this hack does work in Windows
1996                            if dir:
1997 theshadow 1.5                  start_dir1 = start_dir
1998 theshadow 1.1                  if isdir(join(start_dir,default)):
1999                                    start_dir = join(start_dir,default)
2000                                dl = wxDirDialog(self.frame,
2001                                        'Choose a directory to save to, pick a partial download to resume',
2002                                        defaultPath = start_dir, style = wxDD_DEFAULT_STYLE | wxDD_NEW_DIR_BUTTON)
2003                            else:
2004                                dl = wxFileDialog(self.frame,
2005                                        'Choose file to save as, pick a partial download to resume', 
2006                                        defaultDir = start_dir, defaultFile = default, wildcard = '*',
2007                                        style = wxSAVE)
2008                
2009                            if dl.ShowModal() != wxID_OK:
2010                                f.set()
2011                                self.done(None)
2012                                return
2013                
2014                            d = dl.GetPath()
2015 theshadow 1.5              if d == start_dir:
2016                                d = start_dir1
2017 theshadow 1.1              bucket[0] = d
2018                            d1,d2 = split(d)
2019                            if d2 == default:
2020                                d = d1
2021                            self.configfile.WriteLastSaved(d)
2022                
2023                        else:
2024                            bucket[0] = saveas
2025                            default = basename(saveas)
2026                
2027 theshadow 1.41         self.onChooseFileDone(default, size)
2028                        f.set()
2029                
2030                    def ChooseFileDone(self, name, size):
2031                        self.invokeLater(self.onChooseFileDone, [name, size])
2032                
2033                    def onChooseFileDone(self, name, size):
2034 theshadow 1.47         self.torrentsize = size
2035 theshadow 1.41         lname = basename(name)
2036 theshadow 1.47         self.filename = lname
2037 theshadow 1.41         self.fileNameText.SetLabel('%s' % (lname))
2038 theshadow 1.1          self.fileSizeText.SetLabel('(%.2f MiB)' % (float(size) / (1 << 20)))
2039 theshadow 1.37         self.timeText.SetLabel(hours(clock() - self.starttime) + ' / ' + self.activity)
2040 theshadow 1.41         self.fileDestText.SetLabel(name)
2041                        self.frame.SetTitle(lname + '- BitTorrent ' + version)
2042 theshadow 1.1  
2043                        minsize = self.fileNameText.GetBestSize()
2044 theshadow 1.32         if (not self.configfileargs['gui_stretchwindow'] or
2045 theshadow 1.1                              minsize.GetWidth() < self.addwidth):
2046                            minsize.SetWidth(self.addwidth)
2047                        self.fnsizer.SetMinSize (minsize)
2048                        minsize.SetHeight(self.fileSizeText.GetBestSize().GetHeight())
2049                        self.fnsizer2.SetMinSize (minsize)
2050                        minsize.SetWidth(minsize.GetWidth()+(self.FONT*8))
2051                        minsize.SetHeight(self.fileNameText.GetBestSize().GetHeight()+self.fileSizeText.GetBestSize().GetHeight())
2052                        minsize.SetHeight(2*self.errorText.GetBestSize().GetHeight())
2053                        self.errorTextSizer.SetMinSize(minsize)
2054                        self.topboxsizer.SetMinSize(minsize)
2055                
2056                        # Kludge to make details and about catch the event
2057                        self.frame.SetSize ((self.frame.GetSizeTuple()[0]+1, self.frame.GetSizeTuple()[1]+1))
2058                        self.frame.SetSize ((self.frame.GetSizeTuple()[0]-1, self.frame.GetSizeTuple()[1]-1))
2059                        self.colSizer.Fit(self.frame)
2060                        self.frame.Layout()
2061                        self.frame.Refresh()
2062                
2063                    def newpath(self, path):
2064                        self.invokeLater(self.onNewpath, [path])
2065                
2066 theshadow 1.1      def onNewpath(self, path):
2067                        self.fileDestText.SetLabel(path)
2068                
2069                    def pause(self, event):
2070                        self.invokeLater(self.onPause)
2071                
2072                    def onPause(self):
2073 theshadow 1.20         if not self.dow:
2074                            return
2075 theshadow 1.1          if self.ispaused:
2076 theshadow 1.13             self.ispaused = False
2077 theshadow 1.1              self.pauseButton.SetLabel('Pause')
2078                            self.dow.Unpause()
2079                        else:
2080                            if self.dow.Pause():
2081 theshadow 1.13                 self.ispaused = True
2082 theshadow 1.1                  self.pauseButton.SetLabel('Resume')
2083                                self.downRateText.SetLabel(' ')
2084                                self.upRateText.SetLabel(' ')
2085                                self.setStatusIcon('startup')
2086                
2087                    def done(self, event):
2088                        self.uiflag.set()
2089                        self.flag.set()
2090 theshadow 1.13         self.shuttingdown = True
2091 theshadow 1.23         if self.taskbaricon:
2092 theshadow 1.83             try:
2093                                self.frame.tbicon.RemoveIcon()
2094                            except:
2095                                pass
2096 theshadow 1.72             try:
2097                                self.frame.tbicon.Destroy()
2098                            except:
2099                                pass
2100 theshadow 1.1          if (self.detailBox is not None):
2101                            try:
2102                                self.detailBox.Close ()
2103                            except wxPyDeadObjectError, e:
2104                                self.detailBox = None
2105                        if (self.aboutBox is not None):
2106                            try:
2107                                self.aboutBox.Close ()
2108                            except wxPyDeadObjectError, e:
2109                                self.aboutBox = None
2110                        if (self.creditsBox is not None):
2111                            try:
2112                                self.creditsBox.Close ()
2113                            except wxPyDeadObjectError, e:
2114                                self.creditsBox = None
2115                        if (self.advBox is not None):
2116                            try:
2117                                self.advBox.Close ()
2118                            except wxPyDeadObjectError, e:
2119                                self.advBox = None
2120                
2121 theshadow 1.1          if (self.statusIconHelpBox is not None):
2122                            try:
2123                                self.statusIconHelpBox.Close ()
2124                            except wxPyDeadObjectError, e:
2125                                self.statusIconHelpBox = None
2126                        self.configfile.Close()
2127 theshadow 1.80         try:
2128                            self.frame.RemoveIcon()
2129                        except:
2130                            pass
2131 theshadow 1.1          self.frame.Destroy()
2132 theshadow 1.80         self.icon.Destroy()
2133                        self.finicon.Destroy()
2134 theshadow 1.1  
2135                    def exception(self):
2136                        data = StringIO()
2137                        print_exc(file = data)
2138                        print data.getvalue()   # report exception here too
2139                        self.on_errorwindow(data.getvalue())
2140                
2141                    def errorwindow(self, err):
2142                        self.invokeLater(self.on_errorwindow,[err])
2143                
2144                    def on_errorwindow(self, err):
2145                        if self._errorwindow is None:
2146 theshadow 1.56             w = wxFrame(None, -1, 'BITTORRENT ERROR', size = (1,1),
2147                                            style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE)
2148 theshadow 1.1              panel = wxPanel(w, -1)
2149 theshadow 1.41 
2150 theshadow 1.1              sizer = wxFlexGridSizer(cols = 1)
2151                            t = ( 'BitTorrent ' + version + '\n' +
2152                                  'OS: ' + sys.platform + '\n' +
2153                                  'Python version: ' + sys.version + '\n' +
2154                                  'wxWindows version: ' + wxVERSION_STRING + '\n' )
2155                            try:
2156                                t += 'Psyco version: ' + hex(psyco.__version__)[2:] + '\n'
2157                            except:
2158                                pass
2159                            try:
2160                                t += 'Allocation method: ' + self.config['alloc_type']
2161                                if self.dow.storagewrapper.bgalloc_active:
2162                                    t += '*'
2163                                t += '\n'
2164                            except:
2165                                pass
2166                            sizer.Add(wxTextCtrl(panel, -1, t + '\n' + err,
2167                                                size = (500,300), style = wxTE_READONLY|wxTE_MULTILINE))
2168                
2169                            sizer.Add(wxStaticText(panel, -1,
2170 theshadow 1.8                      '\nHelp us iron out the bugs in the engine!'))
2171                            linkMail = wxStaticText(panel, -1,
2172 theshadow 1.22                 'Please report this error to '+report_email)
2173 theshadow 1.13             linkMail.SetFont(wxFont(self.FONT, wxDEFAULT, wxNORMAL, wxNORMAL, True))
2174 theshadow 1.8              linkMail.SetForegroundColour('Blue')
2175                            sizer.Add(linkMail)
2176                
2177                            def maillink(self):
2178 theshadow 1.22                 Thread(target = open_new("mailto:" + report_email
2179 theshadow 1.8                                           + "?subject=autobugreport")).start()
2180                            EVT_LEFT_DOWN(linkMail, maillink)
2181 theshadow 1.1  
2182                            border = wxBoxSizer(wxHORIZONTAL)
2183                            border.Add(sizer, 1, wxEXPAND | wxALL, 4)
2184                
2185                            panel.SetSizer(border)
2186 theshadow 1.13             panel.SetAutoLayout(True)
2187 theshadow 1.1  
2188                            w.Show()
2189                            border.Fit(panel)
2190                            w.Fit()
2191                            self._errorwindow = w
2192                
2193                
2194                class btWxApp(wxApp):
2195                    def __init__(self, x, params):
2196                        self.params = params
2197                        wxApp.__init__(self, x)
2198                
2199                    def OnInit(self):
2200                        doneflag = Event()
2201 theshadow 1.67         self.configfile = configReader()
2202 theshadow 1.1          d = DownloadInfoFrame(doneflag, self.configfile)
2203                        self.SetTopWindow(d.frame)
2204                        if len(self.params) == 0:
2205                            b = wxFileDialog (d.frame, 'Choose .torrent file to use',
2206                                        defaultDir = '', defaultFile = '', wildcard = '*.torrent',
2207                                        style = wxOPEN)
2208                
2209                            if b.ShowModal() == wxID_OK:
2210                                self.params.append (b.GetPath())
2211                
2212                        thread = Thread(target = next, args = [self.params, d, doneflag, self.configfile])
2213 theshadow 1.13         thread.setDaemon(False)
2214 theshadow 1.1          thread.start()
2215                        return 1
2216                
2217                def run(params):
2218 theshadow 1.30     if WXPROFILER:
2219                        import profile, pstats
2220                        p = profile.Profile()
2221                        p.runcall(_run, params)
2222                        log = open('profile_data_wx.'+strftime('%y%m%d%H%M%S')+'.txt','a')
2223                        normalstdout = sys.stdout
2224                        sys.stdout = log
2225                #        pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats()
2226                        pstats.Stats(p).strip_dirs().sort_stats('time').print_stats()
2227                        sys.stdout = normalstdout
2228                    else:
2229                        _run(params)
2230                        
2231                def _run(params):
2232 theshadow 1.1      app = btWxApp(0, params)
2233                    app.MainLoop()
2234                
2235                def next(params, d, doneflag, configfile):
2236                    if PROFILER:
2237                        import profile, pstats
2238                        p = profile.Profile()
2239                        p.runcall(_next, params, d, doneflag, configfile)
2240                        log = open('profile_data.'+strftime('%y%m%d%H%M%S')+'.txt','a')
2241                        normalstdout = sys.stdout
2242                        sys.stdout = log
2243                #        pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats()
2244                        pstats.Stats(p).strip_dirs().sort_stats('time').print_stats()
2245                        sys.stdout = normalstdout
2246                    else:
2247                        _next(params, d, doneflag, configfile)
2248                
2249                def _next(params, d, doneflag, configfile):
2250 theshadow 1.59     err = False
2251 theshadow 1.1      try:
2252 theshadow 1.7          while 1:
2253                            try:            
2254 theshadow 1.32                 config = parse_params(params, configfile.config)
2255 theshadow 1.7              except ValueError, e:
2256                                d.error('error: ' + str(e) + '\nrun with no args for parameter explanations')
2257                                break
2258                            if not config:
2259 theshadow 1.44                 d.displayUsage(get_usage(presets = configfile.config))
2260 theshadow 1.7                  break
2261                
2262                            myid = createPeerID()
2263                            seed(myid)
2264                            
2265                            rawserver = RawServer(doneflag, config['timeout_check_interval'],
2266                                                  config['timeout'], ipv6_enable = config['ipv6_enabled'],
2267 theshadow 1.24                                   failfunc = d.error, errorfunc = d.errorwindow)
2268 theshadow 1.7  
2269 theshadow 1.49             upnp_type = UPnP_test(config['upnp_nat_access'])
2270 theshadow 1.51             while True:
2271                                try:
2272                                    listen_port = rawserver.find_and_bind(config['minport'], config['maxport'],
2273                                                    config['bind'], ipv6_socket_style = config['ipv6_binds_v4'],
2274 theshadow 1.62                                     upnp = upnp_type, randomizer = config['random_port'])
2275 theshadow 1.51                     break
2276                                except socketerror, e:
2277                                    if upnp_type and e == UPnP_ERROR:
2278                                        d.error('WARNING: COULD NOT FORWARD VIA UPnP')
2279                                        upnp_type = 0
2280                                        continue
2281                                    d.error("Couldn't listen - " + str(e))
2282                                    d.failed()
2283                                    return
2284 theshadow 1.36             d.connection_stats = rawserver.get_stats()
2285 theshadow 1.7  
2286                            response = get_response(config['responsefile'], config['url'], d.error)
2287                            if not response:
2288                                break
2289                
2290                            infohash = sha(bencode(response['info'])).digest()
2291                            
2292 theshadow 1.41             torrentdata = configfile.getTorrentData(infohash)
2293                            if torrentdata:
2294                                oldsave = torrentdata.get('saved as')
2295                                d.old_ratesettings = torrentdata.get('rate settings')
2296                                s = torrentdata.get('stats')
2297                                if s:
2298                                    d.old_upload = s['uploaded']
2299                                    d.old_download = s['downloaded']
2300                            else:
2301                                oldsave = None
2302                
2303 theshadow 1.7              dow = BT1Download(d.updateStatus, d.finished, d.error, d.errorwindow, doneflag,
2304 theshadow 1.39                             config, response, infohash, myid, rawserver, listen_port,
2305                                            configfile.getConfigDir())
2306 theshadow 1.7              d.dow = dow
2307 theshadow 1.45 
2308                            if config['gui_saveas_ask'] == 1:
2309                                oldsave = None
2310 theshadow 1.41             if oldsave:
2311                                if not dow.checkSaveLocation(oldsave):
2312                                    oldsave = None
2313                            if oldsave:
2314                                def choosefile(default, size, saveas, dir, oldsave = oldsave):
2315                                    d.ChooseFileDone(oldsave, size)
2316                                    return oldsave
2317 theshadow 1.46             elif config['gui_saveas_ask'] == 0:
2318                                def choosefile(default, size, saveas, dir,
2319                                               spot = config['gui_default_savedir']):
2320                                    spot = os.path.join(spot,default)
2321                                    d.ChooseFileDone(spot, size)
2322                                    return spot
2323 theshadow 1.41             else:
2324                                choosefile = d.chooseFile
2325                            savedas = dow.saveAs(choosefile, d.newpath)
2326                            if not savedas: 
2327 theshadow 1.7                  break
2328                
2329 theshadow 1.39             if not dow.initFiles(old_style = True):
2330 theshadow 1.12                 break
2331 theshadow 1.7              if not dow.startEngine():
2332 theshadow 1.41                 dow.shutdown()
2333 theshadow 1.7                  break
2334                            dow.startRerequester()
2335 theshadow 1.10             dow.autoStats()
2336 theshadow 1.7  
2337 theshadow 1.34             if not dow.am_I_finished():
2338                                d.updateStatus(activity = 'connecting to peers')
2339 theshadow 1.7              rawserver.listen_forever(dow.getPortHandler())
2340 theshadow 1.41 
2341 theshadow 1.63             ratesettings = {
2342 theshadow 1.41                     'rate setting': d.current_ratesetting,
2343                                    'max download rate': config['max_download_rate']
2344                                }
2345 theshadow 1.63             if d.current_ratesetting != 'automatic':
2346                                ratesettings['uploads'] = config['min_uploads']
2347                                ratesettings['max upload rate'] = config['max_upload_rate']
2348 theshadow 1.41             up, dn = dow.get_transfer_stats()
2349 theshadow 1.63             stats = {
2350 theshadow 1.41                     'uploaded': up + d.old_upload,
2351                                    'downloaded': dn + d.old_download
2352 theshadow 1.63                 }
2353                            torrentdata = {
2354                                    'saved as': savedas,
2355                                    'rate settings': ratesettings,
2356                                    'stats': stats
2357 theshadow 1.41                 }
2358                            dow.shutdown(torrentdata)
2359 theshadow 1.7              break
2360 theshadow 1.1      except:
2361 theshadow 1.59         err = True
2362 theshadow 1.1          data = StringIO()
2363                        print_exc(file = data)
2364                        print data.getvalue()   # report exception here too
2365                        d.errorwindow(data.getvalue())
2366 theshadow 1.54     try:
2367                        rawserver.shutdown()
2368                    except:
2369                        pass
2370 theshadow 1.1      if not d.fin:
2371                        d.failed()
2372 theshadow 1.58     if err:
2373 theshadow 1.69         sleep(3600*24*30)   # this will make the app stick in the task manager,
2374                                            # but so be it
2375 theshadow 1.1  
2376                
2377                if __name__ == '__main__':
2378                    if argv[1:] == ['--version']:
2379                        print version
2380 theshadow 1.68         exit(0)
2381 theshadow 1.1      run(argv[1:])

No CVS admin address has been configured
Powered by
ViewCVS 0.9.3