addresses.py (8888B)
1 from typing import TYPE_CHECKING 2 3 from kivy.app import App 4 from kivy.clock import Clock 5 from kivy.factory import Factory 6 from kivy.properties import ObjectProperty 7 from kivy.lang import Builder 8 from decimal import Decimal 9 from kivy.uix.popup import Popup 10 11 from electrum.gui.kivy.i18n import _ 12 from ...util import address_colors 13 14 if TYPE_CHECKING: 15 from ...main_window import ElectrumWindow 16 17 18 Builder.load_string(''' 19 <AddressLabel@Label> 20 text_size: self.width, None 21 halign: 'left' 22 valign: 'top' 23 24 <AddressItem@CardItem> 25 address: '' 26 memo: '' 27 amount: '' 28 status: '' 29 BoxLayout: 30 spacing: '8dp' 31 height: '32dp' 32 orientation: 'vertical' 33 Widget 34 AddressLabel: 35 text: root.address 36 shorten: True 37 Widget 38 AddressLabel: 39 text: (root.amount if root.status == 'Funded' else root.status) + ' ' + root.memo 40 color: .699, .699, .699, 1 41 font_size: '13sp' 42 shorten: True 43 Widget 44 45 <AddressButton@Button>: 46 background_color: 1, .585, .878, 0 47 halign: 'center' 48 text_size: (self.width, None) 49 shorten: True 50 size_hint: 0.5, None 51 default_text: '' 52 text: self.default_text 53 padding: '5dp', '5dp' 54 height: '40dp' 55 text_color: self.foreground_color 56 disabled_color: 1, 1, 1, 1 57 foreground_color: 1, 1, 1, 1 58 canvas.before: 59 Color: 60 rgba: (0.9, .498, 0.745, 1) if self.state == 'down' else self.background_color 61 Rectangle: 62 size: self.size 63 pos: self.pos 64 65 <AddressesDialog@Popup> 66 id: popup 67 title: _('Addresses') 68 message: '' 69 pr_status: 'Pending' 70 show_change: 0 71 show_used: 0 72 on_message: 73 self.update() 74 BoxLayout: 75 id:box 76 padding: '12dp', '12dp', '12dp', '12dp' 77 spacing: '12dp' 78 orientation: 'vertical' 79 BoxLayout: 80 spacing: '6dp' 81 height: self.minimum_height 82 size_hint: 1, None 83 orientation: 'horizontal' 84 AddressFilter: 85 opacity: 1 86 size_hint: 1, None 87 height: self.minimum_height 88 spacing: '5dp' 89 AddressButton: 90 id: search 91 text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change] 92 on_release: 93 root.show_change = (root.show_change + 1) % 3 94 Clock.schedule_once(lambda dt: root.update()) 95 AddressFilter: 96 opacity: 1 97 size_hint: 1, None 98 height: self.minimum_height 99 spacing: '5dp' 100 AddressButton: 101 id: search 102 text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used] 103 on_release: 104 root.show_used = (root.show_used + 1) % 4 105 Clock.schedule_once(lambda dt: root.update()) 106 AddressFilter: 107 opacity: 1 108 size_hint: 1, None 109 height: self.minimum_height 110 spacing: '5dp' 111 canvas.before: 112 Color: 113 rgba: 0.9, 0.9, 0.9, 1 114 AddressButton: 115 id: change 116 text: root.message if root.message else _('Search') 117 on_release: Clock.schedule_once(lambda dt: app.description_dialog(popup)) 118 RecycleView: 119 scroll_type: ['bars', 'content'] 120 bar_width: '15dp' 121 viewclass: 'AddressItem' 122 id: search_container 123 RecycleBoxLayout: 124 orientation: 'vertical' 125 default_size: None, dp(56) 126 default_size_hint: 1, None 127 size_hint_y: None 128 height: self.minimum_height 129 130 <AddressPopup@Popup>: 131 address: '' 132 balance: '' 133 status: '' 134 script_type: '' 135 pk: '' 136 address_color: 1, 1, 1, 1 137 address_background_color: 0.3, 0.3, 0.3, 1 138 BoxLayout: 139 orientation: 'vertical' 140 ScrollView: 141 GridLayout: 142 cols: 1 143 height: self.minimum_height 144 size_hint_y: None 145 padding: '10dp' 146 spacing: '10dp' 147 TopLabel: 148 text: _('Address') 149 RefLabel: 150 color: root.address_color 151 background_color: root.address_background_color 152 data: root.address 153 name: _('Address') 154 GridLayout: 155 cols: 1 156 size_hint_y: None 157 height: self.minimum_height 158 spacing: '10dp' 159 BoxLabel: 160 text: _('Balance') 161 value: root.balance 162 BoxLabel: 163 text: _('Script type') 164 value: root.script_type 165 BoxLabel: 166 text: _('Status') 167 value: root.status 168 TopLabel: 169 text: _('Private Key') 170 RefLabel: 171 data: root.pk 172 name: _('Private key') 173 on_touched: if not self.data: root.do_export(self) 174 Widget: 175 size_hint: 1, 0.1 176 BoxLayout: 177 size_hint: 1, None 178 height: '48dp' 179 Button: 180 size_hint: 0.5, None 181 height: '48dp' 182 text: _('Receive') 183 on_release: root.receive_at() 184 Button: 185 size_hint: 0.5, None 186 height: '48dp' 187 text: _('Close') 188 on_release: root.dismiss() 189 ''') 190 191 192 193 class AddressPopup(Popup): 194 195 def __init__(self, parent, address, balance, status, **kwargs): 196 super(AddressPopup, self).__init__(**kwargs) 197 self.title = _('Address Details') 198 self.parent_dialog = parent 199 self.app = parent.app 200 self.address = address 201 self.status = status 202 self.script_type = self.app.wallet.get_txin_type(self.address) 203 self.balance = self.app.format_amount_and_units(balance) 204 self.address_color, self.address_background_color = address_colors(self.app.wallet, address) 205 206 def receive_at(self): 207 self.dismiss() 208 self.parent_dialog.dismiss() 209 self.app.switch_to('receive') 210 # retry until receive_screen is set 211 Clock.schedule_interval(lambda dt: bool(self.app.receive_screen.set_address(self.address) and False) if self.app.receive_screen else True, 0.1) 212 213 def do_export(self, pk_label): 214 self.app.export_private_keys(pk_label, self.address) 215 216 217 class AddressesDialog(Factory.Popup): 218 219 def __init__(self, app): 220 Factory.Popup.__init__(self) 221 self.app = app # type: ElectrumWindow 222 223 def get_card(self, addr, balance, is_used, label): 224 ci = {} 225 ci['screen'] = self 226 ci['address'] = addr 227 ci['memo'] = label 228 ci['amount'] = self.app.format_amount_and_units(balance) 229 ci['status'] = _('Used') if is_used else _('Funded') if balance > 0 else _('Unused') 230 return ci 231 232 def update(self): 233 wallet = self.app.wallet 234 if self.show_change == 0: 235 _list = wallet.get_receiving_addresses() 236 elif self.show_change == 1: 237 _list = wallet.get_change_addresses() 238 else: 239 _list = wallet.get_addresses() 240 search = self.message 241 container = self.ids.search_container 242 n = 0 243 cards = [] 244 for address in _list: 245 label = wallet.get_label(address) 246 balance = sum(wallet.get_addr_balance(address)) 247 is_used_and_empty = wallet.is_used(address) and balance == 0 248 if self.show_used == 1 and (balance or is_used_and_empty): 249 continue 250 if self.show_used == 2 and balance == 0: 251 continue 252 if self.show_used == 3 and not is_used_and_empty: 253 continue 254 card = self.get_card(address, balance, is_used_and_empty, label) 255 if search and not self.ext_search(card, search): 256 continue 257 cards.append(card) 258 n += 1 259 container.data = cards 260 if not n: 261 self.app.show_error('No address matching your search') 262 263 def show_item(self, obj): 264 address = obj.address 265 c, u, x = self.app.wallet.get_addr_balance(address) 266 balance = c + u + x 267 d = AddressPopup(self, address, balance, obj.status) 268 d.open() 269 270 def ext_search(self, card, search): 271 return card['memo'].find(search) >= 0 or card['amount'].find(search) >= 0