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:])
|