|
|
@ -1,8 +1,11 @@ |
|
|
|
import curses, datetime |
|
|
|
from decimal import Decimal |
|
|
|
_ = lambda x:x |
|
|
|
#from i18n import _ |
|
|
|
from util import format_satoshis, set_verbosity |
|
|
|
|
|
|
|
WIDTH=150 |
|
|
|
|
|
|
|
EMPTY = " "*15 |
|
|
|
|
|
|
|
class ElectrumGui: |
|
|
|
|
|
|
@ -10,14 +13,35 @@ class ElectrumGui: |
|
|
|
self.wallet = wallet |
|
|
|
self.config = config |
|
|
|
self.stdscr = curses.initscr() |
|
|
|
|
|
|
|
#c = self.stdscr.getch() |
|
|
|
#print c |
|
|
|
|
|
|
|
curses.noecho() |
|
|
|
curses.cbreak() |
|
|
|
curses.start_color() |
|
|
|
#curses.start_color() |
|
|
|
self.stdscr.keypad(1) |
|
|
|
self.stdscr.border(0) |
|
|
|
self.maxy, self.maxx = self.stdscr.getmaxyx() |
|
|
|
set_verbosity(False) |
|
|
|
self.tab = 0 |
|
|
|
self.pos = 0 |
|
|
|
self.popup_pos = 0 |
|
|
|
self.w = None |
|
|
|
self.is_popup = False |
|
|
|
|
|
|
|
self.str_recipient = EMPTY |
|
|
|
self.str_description = EMPTY |
|
|
|
self.str_amount = EMPTY |
|
|
|
self.str_fee = EMPTY |
|
|
|
|
|
|
|
self.wallet.interface.register_callback('updated', self.refresh) |
|
|
|
self.wallet.interface.register_callback('connected', self.refresh) |
|
|
|
self.wallet.interface.register_callback('disconnected', self.refresh) |
|
|
|
self.wallet.interface.register_callback('disconnecting', self.refresh) |
|
|
|
self.tab_names = [_("History"), _("Send"), _("Receive"), _("Contacts"), _("Wall")] |
|
|
|
self.num_tabs = len(self.tab_names) |
|
|
|
curses.curs_set(0) |
|
|
|
|
|
|
|
def server_list_changed(self): |
|
|
|
pass |
|
|
@ -26,38 +50,50 @@ class ElectrumGui: |
|
|
|
pass |
|
|
|
|
|
|
|
def print_history(self): |
|
|
|
width = [20, 40, 14, 14] |
|
|
|
delta = (self.maxx - sum(width) - 4)/3 |
|
|
|
format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%"+"%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s" |
|
|
|
|
|
|
|
b = 0 |
|
|
|
messages = [] |
|
|
|
for line in self.wallet.get_tx_history(): |
|
|
|
v = line['value'] |
|
|
|
for tx in self.wallet.get_tx_history(): |
|
|
|
v = tx['value'] |
|
|
|
b += v |
|
|
|
try: |
|
|
|
time_str = str( datetime.datetime.fromtimestamp( line['timestamp'])) |
|
|
|
time_str = str( datetime.datetime.fromtimestamp( tx['timestamp'])) |
|
|
|
except: |
|
|
|
print line['timestamp'] |
|
|
|
print tx['timestamp'] |
|
|
|
time_str = 'pending' |
|
|
|
label = line.get('label') |
|
|
|
if not label: label = line['tx_hash'] |
|
|
|
else: label = label + ' '*(64 - len(label) ) |
|
|
|
messages.append( time_str + " " + label + " " + format_satoshis(v)+ " "+ format_satoshis(b) ) |
|
|
|
tx_hash = tx['tx_hash'] |
|
|
|
|
|
|
|
self.print_list(messages, "%19s %64s %14s %10s"%("Date", "Description", "Amount", "Balance")) |
|
|
|
label = self.wallet.labels.get(tx_hash) |
|
|
|
is_default_label = (label == '') or (label is None) |
|
|
|
if is_default_label: label = tx['default_label'] |
|
|
|
|
|
|
|
#label += ' '*(40 - len(label) ) |
|
|
|
messages.append( format_str%( time_str, label, format_satoshis(v), format_satoshis(b) ) ) |
|
|
|
|
|
|
|
self.print_list(messages[::-1], format_str%( _("Date"), _("Description"), _("Amount"), _("Balance"))) |
|
|
|
|
|
|
|
|
|
|
|
def print_balance(self): |
|
|
|
c, u = self.wallet.get_balance() |
|
|
|
msg = "Balance: %f"%(Decimal( c ) / 100000000) |
|
|
|
if u: |
|
|
|
msg += " [%f unconfirmed]"%(Decimal( u ) / 100000000) |
|
|
|
self.stdscr.addstr( self.maxy -3, 2, msg) |
|
|
|
|
|
|
|
tab_names = ["History", "Send", "Receive", "Contacts"] |
|
|
|
for i in range(4): |
|
|
|
self.stdscr.addstr( 0, 2+9*i, tab_names[i], curses.A_BOLD if self.tab == i else 0) |
|
|
|
if self.wallet.interface and self.wallet.interface.is_connected: |
|
|
|
if not self.wallet.up_to_date: |
|
|
|
msg = _( "Synchronizing..." ) |
|
|
|
else: |
|
|
|
c, u = self.wallet.get_balance() |
|
|
|
msg = _("Balance")+": %f "%(Decimal( c ) / 100000000) |
|
|
|
if u: msg += " [%f unconfirmed]"%(Decimal( u ) / 100000000) |
|
|
|
else: |
|
|
|
msg = _( "Not connected" ) |
|
|
|
|
|
|
|
self.stdscr.addstr( self.maxy -1, self.maxx-30, " Settings Network Quit ") |
|
|
|
self.stdscr.addstr( self.maxy -1, 3, msg) |
|
|
|
|
|
|
|
for i in range(self.num_tabs): |
|
|
|
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) |
|
|
|
|
|
|
|
self.stdscr.addstr( self.maxy -1, self.maxx-30, ' '.join([_("Settings"), _("Network"), _("Quit")])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_contacts(self): |
|
|
|
messages = map(lambda addr: "%30s %30s "%(addr, self.wallet.labels.get(addr,"")), self.wallet.addressbook) |
|
|
@ -69,28 +105,52 @@ class ElectrumGui: |
|
|
|
|
|
|
|
def print_send_dialog(self): |
|
|
|
self.stdscr.clear() |
|
|
|
self.stdscr.addstr( 3, 2, "Pay to") |
|
|
|
self.stdscr.addstr( 5, 2, "Description") |
|
|
|
self.stdscr.addstr( 7, 2, "Amount") |
|
|
|
self.stdscr.addstr( 9, 2, "Fee") |
|
|
|
return |
|
|
|
|
|
|
|
while True: |
|
|
|
curses.echo() |
|
|
|
s = self.stdscr.getstr(3, 15) |
|
|
|
curses.noecho() |
|
|
|
self.stdscr.addstr( 3, 2, _("Pay to")) |
|
|
|
self.stdscr.addstr( 3, 15, self.str_recipient, curses.A_REVERSE if self.pos%5==0 else 0) |
|
|
|
|
|
|
|
if s: break |
|
|
|
pass |
|
|
|
self.stdscr.addstr( 5, 2, _("Description")) |
|
|
|
self.stdscr.addstr( 5, 15, self.str_description, curses.A_REVERSE if self.pos%5==1 else 0) |
|
|
|
|
|
|
|
self.stdscr.addstr( 7, 2, _("Amount")) |
|
|
|
self.stdscr.addstr( 7, 15, self.str_amount, curses.A_REVERSE if self.pos%5==2 else 0) |
|
|
|
|
|
|
|
self.stdscr.addstr( 9, 2, _("Fee")) |
|
|
|
self.stdscr.addstr( 9, 15, self.str_fee, curses.A_REVERSE if self.pos%5==3 else 0) |
|
|
|
|
|
|
|
self.stdscr.addstr( 11, 15, _("Send"), curses.A_REVERSE if self.pos%5==4 else 0) |
|
|
|
|
|
|
|
def exec_send(self): |
|
|
|
curses.curs_set(1) |
|
|
|
curses.echo() |
|
|
|
if self.pos%5==0: |
|
|
|
s = self.stdscr.getstr(3, 15) |
|
|
|
if s: self.str_recipient = s |
|
|
|
elif self.pos%5==1: |
|
|
|
s = self.stdscr.getstr(5, 15) |
|
|
|
if s: self.str_description = s |
|
|
|
elif self.pos%5==2: |
|
|
|
s = self.stdscr.getstr(7, 15) |
|
|
|
if s: self.str_amount = s |
|
|
|
elif self.pos%5==3: |
|
|
|
s = self.stdscr.getstr(9, 15) |
|
|
|
if s: self.str_fee = s |
|
|
|
else: |
|
|
|
pass |
|
|
|
curses.noecho() |
|
|
|
curses.curs_set(0) |
|
|
|
self.print_send_dialog() |
|
|
|
|
|
|
|
def print_banner(self): |
|
|
|
self.stdscr.clear() |
|
|
|
self.stdscr.addstr( 1, 1, self.wallet.banner ) |
|
|
|
|
|
|
|
def print_list(self, list, firstline): |
|
|
|
firstline += " "*(self.maxx -2 - len(firstline)) |
|
|
|
self.stdscr.addstr( 1, 1, firstline ) |
|
|
|
for i in range(self.maxy-6): |
|
|
|
for i in range(self.maxy-4): |
|
|
|
msg = list[i] if i < len(list) else "" |
|
|
|
msg += " "*(self.maxx -2 - len(msg)) |
|
|
|
self.stdscr.addstr( i+2, 1, msg[0:self.maxx - 2]) |
|
|
|
msg += " "*(self.maxx - 2 - len(msg)) |
|
|
|
self.stdscr.addstr( i+2, 1, msg[0:self.maxx - 2], curses.A_REVERSE if i == (self.pos % len(list)) else 0) |
|
|
|
|
|
|
|
def refresh(self): |
|
|
|
self.stdscr.border(0) |
|
|
@ -101,25 +161,61 @@ class ElectrumGui: |
|
|
|
self.print_history() |
|
|
|
self.refresh() |
|
|
|
|
|
|
|
self.is_popup = False |
|
|
|
while 1: |
|
|
|
c = self.stdscr.getch() |
|
|
|
if c == curses.KEY_RIGHT: self.tab = (self.tab + 1)%4 |
|
|
|
if c == curses.KEY_LEFT: self.tab = (self.tab - 1)%4 |
|
|
|
elif c == ord('h'): self.tab = 0 |
|
|
|
elif c == ord('s'): self.tab = 1 |
|
|
|
elif c == ord('r'): self.tab = 2 |
|
|
|
elif c == ord('c'): self.tab = 3 |
|
|
|
elif c == ord('q'): break |
|
|
|
elif c == curses.KEY_HOME: x = y = 0 |
|
|
|
|
|
|
|
if self.tab == 0: |
|
|
|
self.print_history() |
|
|
|
elif self.tab == 1: |
|
|
|
self.print_send_dialog() |
|
|
|
elif self.tab == 2: |
|
|
|
self.print_receive() |
|
|
|
|
|
|
|
if not self.is_popup: |
|
|
|
if c == curses.KEY_RIGHT: self.tab = (self.tab + 1)%self.num_tabs |
|
|
|
elif c == curses.KEY_LEFT: self.tab = (self.tab - 1)%self.num_tabs |
|
|
|
elif c == ord('h'): self.tab = 0 |
|
|
|
elif c == ord('s'): self.tab = 1 |
|
|
|
elif c == ord('r'): self.tab = 2 |
|
|
|
elif c == ord('c'): self.tab = 3 |
|
|
|
|
|
|
|
elif c == curses.KEY_DOWN: self.pos +=1 |
|
|
|
elif c == curses.KEY_UP: self.pos -= 1 |
|
|
|
elif c == 9: self.pos +=1 # tab |
|
|
|
|
|
|
|
elif c in [27, ord('q')]: break |
|
|
|
elif c == 10: self.is_popup = True |
|
|
|
|
|
|
|
elif c == ord('n'): self.network_dialog() |
|
|
|
elif c == ord('s'): self.settings_dialog() |
|
|
|
|
|
|
|
else: |
|
|
|
self.print_contacts() |
|
|
|
if c == 10: self.is_popup = False |
|
|
|
elif c == 27: self.is_popup = False |
|
|
|
elif c == curses.KEY_UP: self.popup_pos -= 1 |
|
|
|
elif c == curses.KEY_DOWN: self.popup_pos +=1 |
|
|
|
#else: raise BaseException("zz %d"%c) |
|
|
|
|
|
|
|
if self.is_popup: |
|
|
|
if self.tab == 0: |
|
|
|
self.context_popup('',["blah","foo"]) |
|
|
|
elif self.tab == 1 and self.pos%5==4: |
|
|
|
self.context_popup('Pay?',["Pay","Cancel"]) |
|
|
|
elif self.tab == 2: |
|
|
|
self.context_popup('', ["blah"]) |
|
|
|
elif self.tab == 3: |
|
|
|
self.context_popup('', ["Pay to"]) |
|
|
|
else: |
|
|
|
self.exec_send() |
|
|
|
self.is_popup = False |
|
|
|
#self.print_send_dialog() |
|
|
|
|
|
|
|
else: |
|
|
|
if self.tab == 0: |
|
|
|
self.print_history() |
|
|
|
elif self.tab == 1: |
|
|
|
self.print_send_dialog() |
|
|
|
elif self.tab == 2: |
|
|
|
self.print_receive() |
|
|
|
elif self.tab == 3: |
|
|
|
self.print_contacts() |
|
|
|
else: |
|
|
|
self.print_banner() |
|
|
|
|
|
|
|
self.refresh() |
|
|
|
|
|
|
|
curses.nocbreak(); |
|
|
@ -127,3 +223,23 @@ class ElectrumGui: |
|
|
|
curses.echo() |
|
|
|
curses.endwin() |
|
|
|
|
|
|
|
def context_popup(self, text, items): |
|
|
|
if not self.w: self.w = curses.newwin(10, 30, 5, 5) |
|
|
|
w = self.w |
|
|
|
w.clear() |
|
|
|
w.border(0) |
|
|
|
|
|
|
|
w.addstr( 2,2,text) |
|
|
|
|
|
|
|
for i in range(len(items)): |
|
|
|
item = items[i] |
|
|
|
w.addstr( 4 + 2*i, 2, item, curses.A_REVERSE if self.popup_pos%(len(items))==i else 0) |
|
|
|
|
|
|
|
|
|
|
|
w.refresh() |
|
|
|
|
|
|
|
def network_dialog(self): |
|
|
|
pass |
|
|
|
|
|
|
|
def settings_dialog(self): |
|
|
|
pass |
|
|
|