1 import curses, datetime
2 from decimal import Decimal
5 from util import format_satoshis, set_verbosity
11 def __init__(self, wallet, config, app=None):
12 self.stdscr = curses.initscr()
16 curses.use_default_colors()
17 curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
20 self.maxy, self.maxx = self.stdscr.getmaxyx()
22 self.w = curses.newwin(10, 50, 5, 5)
31 self.str_recipient = ""
32 self.str_description = ""
36 self.wallet.interface.register_callback('updated', self.refresh)
37 self.wallet.interface.register_callback('connected', self.refresh)
38 self.wallet.interface.register_callback('disconnected', self.refresh)
39 self.wallet.interface.register_callback('disconnecting', self.refresh)
40 self.tab_names = [_("History"), _("Send"), _("Receive"), _("Contacts"), _("Wall")]
41 self.num_tabs = len(self.tab_names)
43 def server_list_changed(self):
46 def restore_or_create(self):
50 def get_string(self, y, x):
53 self.stdscr.addstr( y, x, " "*20, curses.A_REVERSE)
54 s = self.stdscr.getstr(y,x)
60 def print_history(self):
61 width = [20, 40, 14, 14]
62 delta = (self.maxx - sum(width) - 4)/3
63 format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%"+"%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s"
67 for tx in self.wallet.get_tx_history():
68 v = self.wallet.get_tx_value(tx['tx_hash'])
71 time_str = str( datetime.datetime.fromtimestamp( tx['timestamp']))
75 tx_hash = tx['tx_hash']
77 label = self.wallet.labels.get(tx_hash)
78 is_default_label = (label == '') or (label is None)
79 if is_default_label: label = tx['default_label']
81 #label += ' '*(40 - len(label) )
82 messages.append( format_str%( time_str, label, format_satoshis(v), format_satoshis(b) ) )
84 self.print_list(messages[::-1], format_str%( _("Date"), _("Description"), _("Amount"), _("Balance")))
87 def print_balance(self):
88 if self.wallet.interface and self.wallet.interface.is_connected:
89 if not self.wallet.up_to_date:
90 msg = _( "Synchronizing..." )
92 c, u = self.wallet.get_balance()
93 msg = _("Balance")+": %f "%(Decimal( c ) / 100000000)
94 if u: msg += " [%f unconfirmed]"%(Decimal( u ) / 100000000)
96 msg = _( "Not connected" )
98 self.stdscr.addstr( self.maxy -1, 3, msg)
100 for i in range(self.num_tabs):
101 self.stdscr.addstr( 0, 2 + 2*i + len(''.join(self.tab_names[0:i])), ' '+self.tab_names[i]+' ', curses.A_BOLD if self.tab == i else 0)
103 self.stdscr.addstr( self.maxy -1, self.maxx-30, ' '.join([_("Settings"), _("Network"), _("Quit")]))
106 def print_contacts(self):
107 messages = map(lambda addr: "%30s %30s "%(addr, self.wallet.labels.get(addr,"")), self.wallet.addressbook)
108 self.print_list(messages, "%19s %25s "%("Address", "Label"))
110 def print_receive(self):
111 messages = map(lambda addr: "%30s %30s "%(addr, self.wallet.labels.get(addr,"")), self.wallet.addresses)
112 self.print_list(messages, "%19s %25s "%("Address", "Label"))
114 def print_edit_line(self, y, label, text, index, size):
115 text += " "*(size - len(text) )
116 self.stdscr.addstr( y, 2, label)
117 self.stdscr.addstr( y, 15, text, curses.A_REVERSE if self.pos%5==index else curses.color_pair(1))
119 def print_send_tab(self):
121 self.print_edit_line(3, _("Pay to"), self.str_recipient, 0, 40)
122 self.print_edit_line(5, _("Description"), self.str_description, 1, 40)
123 self.print_edit_line(7, _("Amount"), self.str_amount, 2, 15)
124 self.print_edit_line(9, _("Fee"), self.str_fee, 3, 15)
125 self.stdscr.addstr( 12, 15, _("Send"), curses.A_REVERSE if self.pos%5==4 else 0)
127 def getstr_send(self):
131 s = self.stdscr.getstr(3, 15)
132 if s: self.str_recipient = s
134 s = self.stdscr.getstr(5, 15)
135 if s: self.str_description = s
137 s = self.stdscr.getstr(7, 15)
138 if s: self.str_amount = s
140 s = self.stdscr.getstr(9, 15)
141 if s: self.str_fee = s
146 self.print_send_tab()
148 def print_banner(self):
149 self.stdscr.addstr( 1, 1, self.wallet.banner )
151 def print_list(self, list, firstline):
152 firstline += " "*(self.maxx -2 - len(firstline))
153 self.stdscr.addstr( 1, 1, firstline )
154 for i in range(self.maxy-4):
155 msg = list[i] if i < len(list) else ""
156 msg += " "*(self.maxx - 2 - len(msg))
157 self.stdscr.addstr( i+2, 1, msg[0:self.maxx - 2], curses.A_REVERSE if i == (self.pos % len(list)) else 0)
160 self.stdscr.border(0)
162 self.stdscr.refresh()
164 def main_command(self):
165 c = self.stdscr.getch()
166 if c == curses.KEY_RIGHT: self.tab = (self.tab + 1)%self.num_tabs
167 elif c == curses.KEY_LEFT: self.tab = (self.tab - 1)%self.num_tabs
168 elif c == curses.KEY_DOWN: self.pos +=1
169 elif c == curses.KEY_UP: self.pos -= 1
170 elif c == 9: self.pos +=1 # tab
171 elif c in [27, ord('q')]: self.tab = -1
172 elif c == ord('n'): self.network_dialog()
173 elif c == ord('s'): self.settings_dialog()
176 def run_tab(self, i, print_func, exec_func):
181 c = self.main_command()
185 def run_history_tab(self, c):
187 out = self.run_popup('',["blah","foo"])
190 def run_send_tab(self, c):
196 self.print_send_tab()
198 def run_receive_tab(self, c):
200 out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"])
202 def run_contacts_tab(self, c):
204 out = self.run_popup('Adress', ["Copy", "Pay to", "Edit label", "Delete"]).get('button')
205 address = self.wallet.addressbook[self.pos%len(self.wallet.addressbook)]
208 self.str_recipient = address
210 elif out == "Edit label":
211 s = self.get_string(6 + self.pos, 18)
213 self.wallet.labels[address] = s
215 def run_banner_tab(self, c):
220 while self.tab != -1:
221 self.run_tab(0, self.print_history, self.run_history_tab)
222 self.run_tab(1, self.print_send_tab, self.run_send_tab)
223 self.run_tab(2, self.print_receive, self.run_receive_tab)
224 self.run_tab(3, self.print_contacts, self.run_contacts_tab)
225 self.run_tab(4, self.print_banner, self.run_banner_tab)
228 self.stdscr.keypad(0);
235 if not self.wallet.is_valid(self.str_recipient):
236 self.show_message(_('Invalid Bitcoin address'))
239 amount = int( Decimal( self.str_amount) * 100000000 )
241 self.show_message(_('Invalid Amount'))
244 fee = int( Decimal( self.str_fee) * 100000000 )
246 self.show_message(_('Invalid Fee'))
249 if self.wallet.use_encryption:
250 password = self.password_dialog()
257 def show_message(self, message):
261 w.addstr(2,2,message)
263 c = self.stdscr.getch()
266 def run_popup(self, title, items):
267 return self.run_dialog(title, map(lambda x: {'type':'button','label':x}, items), interval=1, y_pos = self.pos+3)
270 def network_dialog(self):
271 out = self.run_dialog('Network', [
272 {'label':'server', 'type':'str', 'value':self.wallet.interface.server},
273 {'label':'proxy', 'type':'str', 'value':self.config.get('proxy')},
276 if out.get('server'): self.wallet.interface.set_server(out.get('server'))
279 def settings_dialog(self):
280 out = self.run_dialog('Settings', [
281 {'label':'Default GUI', 'type':'list', 'choices':['classic','lite','gtk','text'], 'value':self.config.get('gui')},
282 {'label':'Default fee', 'type':'satoshis', 'value':self.config.get('fee')}
285 if out.get('Default GUI'): self.config.set_key('gui', out['Default GUI'], True)
288 def password_dialog(self):
289 out = self.run_dialog('Password', [
290 {'label':'Password', 'type':'str'}
295 def run_dialog(self, title, items, interval=2, buttons=None, y_pos=3):
296 self.w = curses.newwin( 2+len(items)*interval, 50, y_pos, 5)
298 #items.append({'label':'cancel','type':'button'})
299 if buttons: items.append({'label':' ok ','type':'button'})
304 w.addstr( 0, 2, title)
309 label = item.get('label')
310 if item.get('type') == 'list':
311 value = item.get('value','')
312 elif item.get('type') == 'satoshis':
313 value = format_satoshis(item.get('value'))
314 elif item.get('type') == 'str':
315 value = item.get('value','')
319 w.addstr( 1+interval*i, 2, label)
320 w.addstr( 1+interval*i, 15, value, curses.A_REVERSE if self.popup_pos%num==i else curses.color_pair(1) )
322 w.addstr( 1+interval*i, 2, label, curses.A_REVERSE if self.popup_pos%num==i else 0)
326 c = self.stdscr.getch()
327 if c in [ord('q'), 27]: break
328 elif c == curses.KEY_UP: self.popup_pos -= 1
329 elif c == curses.KEY_DOWN: self.popup_pos +=1
331 i = self.popup_pos%num
333 _type = item.get('type')
335 item['value'] = self.get_string(2+2*i, 15)
336 out[item.get('label')] = item.get('value')
338 elif _type == 'satoshis':
341 s = w.getstr(2+2*i, 15)
345 s = int( Decimal(s)*100000000 )
347 out[item.get('label')] = item.get('value')
350 elif _type == 'list':
351 choices = item.get('choices')
353 j = choices.index(item.get('value'))
356 new_choice = choices[(j + 1)% len(choices)]
357 item['value'] = new_choice
358 out[item.get('label')] = item.get('value')
361 elif _type == 'button':
362 out['button'] = item.get('label')