URI: 
       tnew combined tablet&mobile design on top of 1.9.x branch WIP - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 30126c544b81bcb8ac6a510da9c9d42acf82ee28
   DIR parent 2a889d318be1e2d50945f49b565ccd09cebd69ff
  HTML Author: qua-non <akshayaurora@gmail.com>
       Date:   Tue, 18 Feb 2014 12:42:57 +0530
       
       new combined tablet&mobile design on top of 1.9.x branch WIP
       
       Diffstat:
         A gui/kivy/Makefile                   |      29 +++++++++++++++++++++++++++++
         A gui/kivy/Readme.txt                 |       5 +++++
         A gui/kivy/__init__.py                |      87 +++++++++++++++++++++++++++++++
         A gui/kivy/carousel.py                |      41 +++++++++++++++++++++++++++++++
         A gui/kivy/combobox.py                |      93 +++++++++++++++++++++++++++++++
         A gui/kivy/console.py                 |     319 +++++++++++++++++++++++++++++++
         A gui/kivy/dialog.py                  |     611 +++++++++++++++++++++++++++++++
         A gui/kivy/drawer.py                  |     174 +++++++++++++++++++++++++++++++
         A gui/kivy/gridview.py                |     203 +++++++++++++++++++++++++++++++
         A gui/kivy/installwizard.py           |     224 +++++++++++++++++++++++++++++++
         A gui/kivy/main.kv                    |     402 ++++++++++++++++++++++++++++++
         A gui/kivy/main_window.py             |     294 +++++++++++++++++++++++++++++++
         A gui/kivy/menus.py                   |      95 ++++++++++++++++++++++++++++++
         A gui/kivy/nfc_scanner/__init__.py    |      43 ++++++++++++++++++++++++++++++
         A gui/kivy/nfc_scanner/scanner_andro… |      86 ++++++++++++++++++++++++++++++
         A gui/kivy/nfc_scanner/scanner_dummy… |      37 +++++++++++++++++++++++++++++++
         A gui/kivy/qr_scanner/__init__.py     |     105 +++++++++++++++++++++++++++++++
         A gui/kivy/qr_scanner/scanner_androi… |     354 +++++++++++++++++++++++++++++++
         A gui/kivy/qr_scanner/scanner_camera… |      89 +++++++++++++++++++++++++++++++
         A gui/kivy/qrcodewidget.py            |     179 +++++++++++++++++++++++++++++++
         A gui/kivy/screens.py                 |    1095 +++++++++++++++++++++++++++++++
         A gui/kivy/statusbar.py               |       7 +++++++
         A gui/kivy/textinput.py               |      14 ++++++++++++++
         A gui/kivy/theming/light-0.png        |       0 
         A gui/kivy/theming/light-1.png        |       0 
         A gui/kivy/theming/light.atlas        |       2 ++
         A gui/kivy/theming/light/action_bar.… |       0 
         A gui/kivy/theming/light/action_grou… |       0 
         A gui/kivy/theming/light/add_contact… |       0 
         A gui/kivy/theming/light/arrow_back.… |       0 
         A gui/kivy/theming/light/blue_bg_rou… |       0 
         A gui/kivy/theming/light/btn_create_… |       0 
         A gui/kivy/theming/light/btn_create_… |       0 
         A gui/kivy/theming/light/btn_nfc.png  |       0 
         A gui/kivy/theming/light/btn_send_ad… |       0 
         A gui/kivy/theming/light/btn_send_nf… |       0 
         A gui/kivy/theming/light/card.png     |       0 
         A gui/kivy/theming/light/card_bottom… |       0 
         A gui/kivy/theming/light/card_btn.png |       0 
         A gui/kivy/theming/light/card_top.png |       0 
         A gui/kivy/theming/light/carousel_de… |       0 
         A gui/kivy/theming/light/carousel_se… |       0 
         A gui/kivy/theming/light/clock1.png   |       0 
         A gui/kivy/theming/light/clock2.png   |       0 
         A gui/kivy/theming/light/clock3.png   |       0 
         A gui/kivy/theming/light/clock4.png   |       0 
         A gui/kivy/theming/light/clock5.png   |       0 
         A gui/kivy/theming/light/close.png    |       0 
         A gui/kivy/theming/light/closebutton… |       0 
         A gui/kivy/theming/light/confirmed.p… |       0 
         A gui/kivy/theming/light/contact.png  |       0 
         A gui/kivy/theming/light/create_act_… |       0 
         A gui/kivy/theming/light/create_act_… |       0 
         A gui/kivy/theming/light/dialog.png   |       0 
         A gui/kivy/theming/light/electrum_ic… |       0 
         A gui/kivy/theming/light/error.png    |       0 
         A gui/kivy/theming/light/gear.png     |       0 
         A gui/kivy/theming/light/globe.png    |       0 
         A gui/kivy/theming/light/icon_border… |       0 
         A gui/kivy/theming/light/important.p… |       0 
         A gui/kivy/theming/light/info.png     |       0 
         A gui/kivy/theming/light/lightblue_b… |       0 
         A gui/kivy/theming/light/logo.png     |       0 
         A gui/kivy/theming/light/logo_atom_d… |       0 
         A gui/kivy/theming/light/mail_icon.p… |       0 
         A gui/kivy/theming/light/manualentry… |       0 
         A gui/kivy/theming/light/network.png  |       0 
         A gui/kivy/theming/light/nfc.png      |       0 
         A gui/kivy/theming/light/nfc_clock.p… |       0 
         A gui/kivy/theming/light/nfc_phone.p… |       0 
         A gui/kivy/theming/light/nfc_stage_o… |       0 
         A gui/kivy/theming/light/paste_icon.… |       0 
         A gui/kivy/theming/light/pen.png      |       0 
         A gui/kivy/theming/light/qrcode.png   |       0 
         A gui/kivy/theming/light/settings.png |       0 
         A gui/kivy/theming/light/shadow.png   |       0 
         A gui/kivy/theming/light/shadow_righ… |       0 
         A gui/kivy/theming/light/star_big_in… |       0 
         A gui/kivy/theming/light/stepper_ful… |       0 
         A gui/kivy/theming/light/stepper_lef… |       0 
         A gui/kivy/theming/light/tab.png      |       0 
         A gui/kivy/theming/light/tab_btn.png  |       0 
         A gui/kivy/theming/light/tab_btn_dis… |       0 
         A gui/kivy/theming/light/tab_btn_pre… |       0 
         A gui/kivy/theming/light/tab_disable… |       0 
         A gui/kivy/theming/light/tab_strip.p… |       0 
         A gui/kivy/theming/light/textinput_a… |       0 
         A gui/kivy/theming/light/unconfirmed… |       0 
         A gui/kivy/theming/light/wallet.png   |       0 
         A gui/kivy/theming/light/wallets.png  |       0 
         A gui/kivy/theming/light/white_bg_ro… |       0 
         A gui/kivy/theming/loading.gif        |       0 
         A gui/kivy/theming/splash.png         |       0 
         A gui/kivy/utils.py                   |       2 ++
       
       94 files changed, 4590 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/gui/kivy/Makefile b/gui/kivy/Makefile
       t@@ -0,0 +1,29 @@
       +PYTHON = python
       +# needs kivy installed or in PYTHONPATH
       +
       +.PHONY: theming apk clean
       +
       +theming:
       +        $(PYTHON) -m kivy.atlas theming/light 1024 theming/light/*.png
       +apk:
       +        # running pre build setup
       +        @cp build/buildozer.spec ../../buildozer.spec
       +        # get aes.py
       +        @cd ../..; wget -4 https://raw.github.com/devrandom/slowaes/master/python/aes.py
       +        # rename electrum to main.py
       +        @mv ../../electrum ../../main.py
       +        @-if [ ! -d "../../.buildozer" ];then \
       +                cd ../..; buildozer android debug;\
       +                cp -f gui/kivy/build/blacklist.txt .buildozer/android/platform/python-for-android/src/blacklist.txt;\
       +                rm -rf ./.buildozer/android/platform/python-for-android/dist;\
       +        fi
       +        @-cd ../..; buildozer android debug deploy run
       +        @make clean
       +clean:
       +        # Cleaning up
       +        # remove aes
       +        @-rm ../../aes.py
       +        # rename main.py to electrum
       +        @-mv ../../main.py ../../electrum
       +        # remove buildozer.spec
       +        @-rm ../../buildozer.spec
   DIR diff --git a/gui/kivy/Readme.txt b/gui/kivy/Readme.txt
       t@@ -0,0 +1,5 @@
       +Commands::
       +
       +    `make theming` to make a atlas out of a list of pngs
       +
       +    `make apk` to make a apk
   DIR diff --git a/gui/kivy/__init__.py b/gui/kivy/__init__.py
       t@@ -0,0 +1,87 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2012 thomasv@gitorious
       +#
       +# This program is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU General Public License as published by
       +# the Free Software Foundation, either version 3 of the License, or
       +# (at your option) any later version.
       +#
       +# This program is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       +# GNU General Public License for more details.
       +#
       +# You should have received a copy of the GNU General Public License
       +# along with this program. If not, see <http://www.gnu.org/licenses/>.
       +#
       +# Kivy GUI
       +
       +import sys
       +#, time, datetime, re, threading
       +#from electrum.i18n import _, set_language
       +#from electrum.util import print_error, print_msg, parse_url
       +
       +#:TODO: replace this with kivy's own plugin managment
       +#from electrum.plugins import run_hook
       +#import os.path, json, ast, traceback
       +#import shutil
       +
       +try:
       +    sys.argv = ['']
       +    import kivy
       +except ImportError:
       +    # This error ideally shouldn't raised with pre-built packages
       +    sys.exit("Error: Could not import kivy. Please install it using the" + \
       +             "instructions mentioned here `http://kivy.org/#download` .")
       +
       +# minimum required version for kivy
       +kivy.require('1.8.0')
       +from kivy.logger import Logger
       +
       +from electrum.bitcoin import MIN_RELAY_TX_FEE
       +
       +#:TODO main window
       +from main_window import ElectrumWindow
       +from electrum.plugins import init_plugins
       +
       +#:TODO find a equivalent method to register to `bitcoin:` uri
       +#: ref: http://stackoverflow.com/questions/30931/register-file-extensions-mime-types-in-linux
       +#class OpenFileEventFilter(object):
       +#    def __init__(self, windows):
       +#        self.windows = windows
       +#        super(OpenFileEventFilter, self).__init__()
       +#
       +#    def eventFilter(self, obj, event):
       +#        if event.type() == QtCore.QEvent.FileOpen:
       +#            if len(self.windows) >= 1:
       +#                self.windows[0].set_url(event.url().toEncoded())
       +#                return True
       +#        return False
       +
       +
       +class ElectrumGui:
       +
       +    def __init__(self, config, network, app=None):
       +        Logger.debug('ElectrumGUI: initialising')
       +        self.network = network
       +        self.config = config
       +
       +        #:TODO
       +        # implement kivy plugin mechanism that needs to be more extensible
       +        # and integrated into the ui so can't be common with existing plugin
       +        # base
       +        #init_plugins(self)
       +
       +
       +    def main(self, url):
       +        ''' The main entry point of the kivy ux
       +        :param url: 'bitcoin:' uri as mentioned in bip0021
       +        :type url: str
       +        :ref: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
       +        '''
       +
       +        self.main_window = w = ElectrumWindow(config=self.config,
       +                                              network=self.network)
       +        w.run()
   DIR diff --git a/gui/kivy/carousel.py b/gui/kivy/carousel.py
       t@@ -0,0 +1,40 @@
       +from kivy.uix.carousel import Carousel
       +from kivy.clock import Clock
       +
       +class CCarousel(Carousel):
       +
       +    def on_touch_move(self, touch):
       +        if self._get_uid('cavoid') in touch.ud:
       +            return
       +        if self._touch is not touch:
       +            super(Carousel, self).on_touch_move(touch)
       +            return self._get_uid() in touch.ud
       +        if touch.grab_current is not self:
       +            return True
       +        ud = touch.ud[self._get_uid()]
       +        direction = self.direction
       +        if ud['mode'] == 'unknown':
       +            if direction[0] in ('r', 'l'):
       +                distance = abs(touch.ox - touch.x)
       +            else:
       +                distance = abs(touch.oy - touch.y)
       +            if distance > self.scroll_distance:
       +                Clock.unschedule(self._change_touch_mode)
       +                ud['mode'] = 'scroll'
       +        else:
       +            diff = 0
       +            if direction[0] in ('r', 'l'):
       +                diff = touch.dx
       +            if direction[0] in ('t', 'b'):
       +                diff = touch.dy
       +
       +            self._offset += diff * 1.27
       +        return True
       +
       +if __name__ == "__main__":
       +    from kivy.app import runTouchApp
       +    from kivy.uix.button import Button
       +    cc = CCarousel()
       +    for i in range(10):
       +        cc.add_widget(Button(text=str(i)))
       +    runTouchApp(cc)
       +\ No newline at end of file
   DIR diff --git a/gui/kivy/combobox.py b/gui/kivy/combobox.py
       t@@ -0,0 +1,93 @@
       +'''
       +ComboBox
       +=======
       +
       +Based on Spinner
       +'''
       +
       +__all__ = ('ComboBox', 'ComboBoxOption')
       +
       +from kivy.properties import ListProperty, ObjectProperty, BooleanProperty
       +from kivy.uix.button import Button
       +from kivy.uix.dropdown import DropDown
       +from kivy.lang import Builder
       +
       +
       +Builder.load_string('''
       +<ComboBoxOption>:
       +    size_hint_y: None
       +    height: 44
       +
       +<ComboBox>:
       +    background_normal: 'atlas://data/images/defaulttheme/spinner'
       +    background_down: 'atlas://data/images/defaulttheme/spinner_pressed'
       +    on_key:
       +        if self.items: x, y = zip(*self.items); self.text = y[x.index(args[1])]
       +''')
       +
       +
       +class ComboBoxOption(Button):
       +    pass
       +
       +
       +class ComboBox(Button):
       +    items = ListProperty()
       +    key = ObjectProperty()
       +
       +    option_cls = ObjectProperty(ComboBoxOption)
       +
       +    dropdown_cls = ObjectProperty(DropDown)
       +
       +    is_open = BooleanProperty(False)
       +
       +    def __init__(self, **kwargs):
       +        self._dropdown = None
       +        super(ComboBox, self).__init__(**kwargs)
       +        self.items_dict = dict(self.items)
       +        self.bind(
       +            on_release=self._toggle_dropdown,
       +            dropdown_cls=self._build_dropdown,
       +            option_cls=self._build_dropdown,
       +            items=self._update_dropdown,
       +            key=self._update_text)
       +        self._build_dropdown()
       +        self._update_text()
       +
       +    def _update_text(self, *largs):
       +        try:
       +            self.text = self.items_dict[self.key]
       +        except KeyError:
       +            pass
       +
       +    def _build_dropdown(self, *largs):
       +        if self._dropdown:
       +            self._dropdown.unbind(on_select=self._on_dropdown_select)
       +            self._dropdown.dismiss()
       +            self._dropdown = None
       +        self._dropdown = self.dropdown_cls()
       +        self._dropdown.bind(on_select=self._on_dropdown_select)
       +        self._update_dropdown()
       +
       +    def _update_dropdown(self, *largs):
       +        dp = self._dropdown
       +        cls = self.option_cls
       +        dp.clear_widgets()
       +        for key, value in self.items:
       +            item = cls(text=value)
       +            # extra attribute
       +            item.key = key
       +            item.bind(on_release=lambda option: dp.select(option.key))
       +            dp.add_widget(item)
       +
       +    def _toggle_dropdown(self, *largs):
       +        self.is_open = not self.is_open
       +
       +    def _on_dropdown_select(self, instance, data, *largs):
       +        self.key = data
       +        self.is_open = False
       +
       +    def on_is_open(self, instance, value):
       +        if value:
       +            self._dropdown.open(self)
       +        else:
       +            self._dropdown.dismiss()
   DIR diff --git a/gui/kivy/console.py b/gui/kivy/console.py
       t@@ -0,0 +1,319 @@
       +# source: http://stackoverflow.com/questions/2758159/how-to-embed-a-python-interpreter-in-a-pyqt-widget
       +
       +import sys, os, re
       +import traceback, platform
       +from kivy.core.window import Keyboard
       +from kivy.uix.textinput import TextInput
       +from kivy.properties import StringProperty, ListProperty, DictProperty
       +from kivy.clock import Clock
       +
       +from electrum import util
       +
       +
       +if platform.system() == 'Windows':
       +    MONOSPACE_FONT = 'Lucida Console'
       +elif platform.system() == 'Darwin':
       +    MONOSPACE_FONT = 'Monaco'
       +else:
       +    MONOSPACE_FONT = 'monospace'
       +
       +
       +class Console(TextInput):
       +
       +    prompt = StringProperty('>> ')
       +    '''String representing the Prompt message'''
       +
       +    startup_message = StringProperty('')
       +    '''Startup Message to be displayed in the Console if any'''
       +
       +    history = ListProperty([])
       +    '''History of the console'''
       +
       +    namespace = DictProperty({})
       +    '''Dict representing the current namespace of the console'''
       +
       +    def __init__(self, **kwargs):
       +        super(Console, self).__init__(**kwargs)
       +        self.construct = []
       +        self.showMessage(self.startup_message)
       +        self.updateNamespace({'run':self.run_script})
       +        self.set_json(False)
       +
       +    def set_json(self, b):
       +        self.is_json = b
       +
       +    def run_script(self, filename):
       +        with open(filename) as f:
       +            script = f.read()
       +        result = eval(script, self.namespace, self.namespace)
       +
       +    def updateNamespace(self, namespace):
       +        self.namespace.update(namespace)
       +
       +    def showMessage(self, message):
       +        self.appendPlainText(message)
       +        self.newPrompt()
       +
       +    def clear(self):
       +        self.setPlainText('')
       +        self.newPrompt()
       +
       +    def newPrompt(self):
       +        if self.construct:
       +            prompt = '.' * len(self.prompt)
       +        else:
       +            prompt = self.prompt
       +
       +        self.completions_pos = self.cursor_index()
       +        self.completions_visible = False
       +
       +        self.appendPlainText(prompt)
       +        self.move_cursor_to('end')
       +
       +    def getCommand(self):
       +        curr_line = self._lines[-1]
       +        curr_line = curr_line.rstrip()
       +        curr_line = curr_line[len(self.prompt):]
       +        return curr_line
       +
       +    def setCommand(self, command):
       +        if self.getCommand() == command:
       +            return
       +        curr_line = self._lines[-1]
       +        last_pos = len(self.text)
       +        self.select_text(last_pos - len(curr_line) + len(self.prompt), last_pos)
       +        self.delete_selection()
       +        self.insert_text(command)
       +
       +    def show_completions(self, completions):
       +        if self.completions_visible:
       +            self.hide_completions()
       +
       +        self.move_cursor_to(self.completions_pos)
       +
       +        completions = map(lambda x: x.split('.')[-1], completions)
       +        t = '\n' + ' '.join(completions)
       +        if len(t) > 500:
       +            t = t[:500] + '...'
       +        self.insert_text(t)
       +        self.completions_end = self.cursor_index()
       +
       +        self.move_cursor_to('end')
       +        self.completions_visible = True
       +
       +
       +    def hide_completions(self):
       +        if not self.completions_visible:
       +            return
       +        self.move_cursor_to(self.completions_pos)
       +        l = self.completions_end - self.completions_pos
       +        for x in range(l):
       +            self.move_cursor_to('cursor_right')
       +            self.do_backspace()
       +
       +        self.move_cursor_to('end')
       +        self.completions_visible = False
       +
       +    def getConstruct(self, command):
       +        if self.construct:
       +            prev_command = self.construct[-1]
       +            self.construct.append(command)
       +            if not prev_command and not command:
       +                ret_val = '\n'.join(self.construct)
       +                self.construct = []
       +                return ret_val
       +            else:
       +                return ''
       +        else:
       +            if command and command[-1] == (':'):
       +                self.construct.append(command)
       +                return ''
       +            else:
       +                return command
       +
       +    def getHistory(self):
       +        return self.history
       +
       +    def setHisory(self, history):
       +        self.history = history
       +
       +    def addToHistory(self, command):
       +        if command and (not self.history or self.history[-1] != command):
       +            self.history.append(command)
       +        self.history_index = len(self.history)
       +
       +    def getPrevHistoryEntry(self):
       +        if self.history:
       +            self.history_index = max(0, self.history_index - 1)
       +            return self.history[self.history_index]
       +        return ''
       +
       +    def getNextHistoryEntry(self):
       +        if self.history:
       +            hist_len = len(self.history)
       +            self.history_index = min(hist_len, self.history_index + 1)
       +            if self.history_index < hist_len:
       +                return self.history[self.history_index]
       +        return ''
       +
       +    def getCursorPosition(self):
       +        return self.cursor[0] - len(self.prompt)
       +
       +    def setCursorPosition(self, position):
       +        self.cursor = (len(self.prompt) + position, self.cursor[1])
       +
       +    def register_command(self, c, func):
       +        methods = { c: func}
       +        self.updateNamespace(methods)
       +
       +
       +    def runCommand(self):
       +        command = self.getCommand()
       +        self.addToHistory(command)
       +
       +        command = self.getConstruct(command)
       +
       +        if command:
       +            tmp_stdout = sys.stdout
       +
       +            class stdoutProxy():
       +                def __init__(self, write_func):
       +                    self.write_func = write_func
       +                    self.skip = False
       +
       +                def flush(self):
       +                    pass
       +
       +                def write(self, text):
       +                    if not self.skip:
       +                        stripped_text = text.rstrip('\n')
       +                        self.write_func(stripped_text)
       +                    self.skip = not self.skip
       +
       +            if type(self.namespace.get(command)) == type(lambda:None):
       +                self.appendPlainText("'%s' is a function. Type '%s()' to use it in the Python console."%(command, command))
       +                self.newPrompt()
       +                return
       +
       +            sys.stdout = stdoutProxy(self.appendPlainText)
       +            try:
       +                try:
       +                    result = eval(command, self.namespace, self.namespace)
       +                    if result != None:
       +                        if self.is_json:
       +                            util.print_json(result)
       +                        else:
       +                            self.appendPlainText(repr(result))
       +                except SyntaxError:
       +                    exec command in self.namespace
       +            except SystemExit:
       +                pass
       +            except:
       +                traceback_lines = traceback.format_exc().split('\n')
       +                # Remove traceback mentioning this file, and a linebreak
       +                for i in (3,2,1,-1):
       +                    traceback_lines.pop(i)
       +                self.appendPlainText('\n'.join(traceback_lines))
       +            sys.stdout = tmp_stdout
       +        self.newPrompt()
       +        self.set_json(False)
       +
       +    def _keyboard_on_key_down(self, window, keycode, text, modifiers):
       +        self._hide_cut_copy_paste()
       +        is_osx = sys.platform == 'darwin'
       +        # Keycodes on OSX:
       +        ctrl, cmd = 64, 1024
       +        key, key_str = keycode
       +
       +        if key == Keyboard.keycodes['tab']:
       +            self.completions()
       +            return
       +
       +        self.hide_completions()
       +
       +        if key == Keyboard.keycodes['enter']:
       +            self.runCommand()
       +            return
       +        if key == Keyboard.keycodes['home']:
       +            self.setCursorPosition(0)
       +            return
       +        if key == Keyboard.keycodes['pageup']:
       +            return
       +        elif key in (Keyboard.keycodes['left'], Keyboard.keycodes['backspace']):
       +            if self.getCursorPosition() == 0:
       +                return
       +        elif key == Keyboard.keycodes['up']:
       +            self.setCommand(self.getPrevHistoryEntry())
       +            return
       +        elif key == Keyboard.keycodes['down']:
       +            self.setCommand(self.getNextHistoryEntry())
       +            return
       +        elif key == Keyboard.keycodes['l'] and modifiers == ['ctrl']:
       +            self.clear()
       +
       +        super(Console, self)._keyboard_on_key_down(window, keycode, text, modifiers)
       +
       +    def completions(self):
       +        cmd = self.getCommand()
       +        lastword = re.split(' |\(|\)',cmd)[-1]
       +        beginning = cmd[0:-len(lastword)]
       +
       +        path = lastword.split('.')
       +        ns = self.namespace.keys()
       +
       +        if len(path) == 1:
       +            ns = ns
       +            prefix = ''
       +        else:
       +            obj = self.namespace.get(path[0])
       +            prefix = path[0] + '.'
       +            ns = dir(obj)
       +
       +
       +        completions = []
       +        for x in ns:
       +            if x[0] == '_':continue
       +            xx = prefix + x
       +            if xx.startswith(lastword):
       +                completions.append(xx)
       +        completions.sort()
       +
       +        if not completions:
       +            self.hide_completions()
       +        elif len(completions) == 1:
       +            self.hide_completions()
       +            self.setCommand(beginning + completions[0])
       +        else:
       +            # find common prefix
       +            p = os.path.commonprefix(completions)
       +            if len(p)>len(lastword):
       +                self.hide_completions()
       +                self.setCommand(beginning + p)
       +            else:
       +                self.show_completions(completions)
       +
       +    # NEW
       +    def setPlainText(self, message):
       +        """Equivalent to QT version"""
       +        self.text = message
       +
       +    # NEW
       +    def appendPlainText(self, message):
       +        """Equivalent to QT version"""
       +        if len(self.text) == 0:
       +            self.text = message
       +        else:
       +            if message:
       +                self.text += '\n' + message
       +
       +    # NEW
       +    def move_cursor_to(self, pos):
       +        """Aggregate all cursor moving functions"""
       +        if isinstance(pos, int):
       +            self.cursor = self.get_cursor_from_index(pos)
       +        elif pos in ('end', 'pgend', 'pageend'):
       +            def updt_cursor(*l):
       +                self.cursor = self.get_cursor_from_index(self.text)
       +            Clock.schedule_once(updt_cursor)
       +        else:  # cursor_home, cursor_end, ... (see docs)
       +            self.do_cursor_movement(pos)
   DIR diff --git a/gui/kivy/dialog.py b/gui/kivy/dialog.py
       t@@ -0,0 +1,611 @@
       +from functools import partial
       +
       +from kivy.app import App
       +from kivy.factory import Factory
       +from kivy.uix.button import Button
       +from kivy.uix.bubble import Bubble
       +from kivy.uix.popup import Popup
       +from kivy.uix.widget import Widget
       +from kivy.uix.carousel import Carousel
       +from kivy.uix.tabbedpanel import TabbedPanelHeader
       +from kivy.properties import (NumericProperty, StringProperty, ListProperty,
       +                             ObjectProperty, AliasProperty, OptionProperty,
       +                             BooleanProperty)
       +
       +from kivy.animation import Animation
       +from kivy.core.window import Window
       +from kivy.clock import Clock
       +from kivy.lang import Builder
       +from kivy.metrics import dp, inch
       +
       +#from electrum.bitcoin import is_valid
       +from electrum.i18n import _
       +
       +# Delayed inits
       +QRScanner = None
       +NFCSCanner = None
       +ScreenAddress = None
       +decode_uri = None
       +
       +DEFAULT_PATH = '/tmp/'
       +app = App.get_running_app()
       +
       +class CarouselHeader(TabbedPanelHeader):
       +
       +    slide = NumericProperty(0)
       +    ''' indicates the link to carousels slide'''
       +
       +class AnimatedPopup(Popup):
       +
       +    def open(self):
       +        self.opacity = 0
       +        super(AnimatedPopup, self).open()
       +        anim = Animation(opacity=1, d=.5).start(self)
       +
       +    def dismiss(self):
       +        def on_complete(*l):
       +            super(AnimatedPopup, self).dismiss()
       +        anim = Animation(opacity=0, d=.5)
       +        anim.bind(on_complete=on_complete)
       +        anim.start(self)
       +
       +
       +class CarouselDialog(AnimatedPopup):
       +    ''' A Popup dialog with a CarouselIndicator used as the content.
       +    '''
       +
       +    carousel_content = ObjectProperty(None)
       +
       +    def open(self):
       +        self.opacity = 0
       +        super(CarouselDialog, self).open()
       +        anim = Animation(opacity=1, d=.5).start(self)
       +
       +    def dismiss(self):
       +        def on_complete(*l):
       +            super(CarouselDialog, self).dismiss()
       +        anim = Animation(opacity=0, d=.5)
       +        anim.bind(on_complete=on_complete)
       +        anim.start(self)
       +
       +    def add_widget(self, widget, index=0):
       +        if isinstance(widget, Carousel):
       +            super(CarouselDialog, self).add_widget(widget, index)
       +            return
       +        if 'carousel_content' not in self.ids.keys():
       +            super(CarouselDialog, self).add_widget(widget)
       +            return
       +        self.carousel_content.add_widget(widget, index)
       +
       +
       +
       +class NFCTransactionDialog(AnimatedPopup):
       +
       +    mode = OptionProperty('send', options=('send','receive'))
       +
       +    scanner = ObjectProperty(None)
       +
       +    def __init__(self, **kwargs):
       +        # Delayed Init
       +        global NFCSCanner
       +        if NFCSCanner is None:
       +            from electrum_gui.kivy.nfc_scanner import NFCScanner
       +        self.scanner = NFCSCanner
       +
       +        super(NFCTransactionDialog, self).__init__(**kwargs)
       +        self.scanner.nfc_init()
       +        self.scanner.bind()
       +
       +    def on_parent(self, instance, value):
       +        sctr = self.ids.sctr
       +        if value:
       +            def _cmp(*l):
       +                anim = Animation(rotation=2, scale=1, opacity=1)
       +                anim.start(sctr)
       +                anim.bind(on_complete=_start)
       +
       +            def _start(*l):
       +                anim = Animation(rotation=350, scale=2, opacity=0)
       +                anim.start(sctr)
       +                anim.bind(on_complete=_cmp)
       +            _start()
       +            return
       +        Animation.cancel_all(sctr)
       +
       +
       +class InfoBubble(Bubble):
       +    '''Bubble to be used to display short Help Information'''
       +
       +    message = StringProperty(_('Nothing set !'))
       +    '''Message to be displayed defaults to "nothing set"'''
       +
       +    icon = StringProperty('')
       +    ''' Icon to be displayed along with the message defaults to ''
       +    '''
       +
       +    fs = BooleanProperty(False)
       +    ''' Show Bubble in half screen mode
       +    '''
       +
       +    modal = BooleanProperty(False)
       +    ''' Allow bubble to be hidden on touch.
       +    '''
       +
       +    dim_background = BooleanProperty(False)
       +    ''' Whether to draw a background on the windows behind the bubble
       +    '''
       +
       +    def on_touch_down(self, touch):
       +        if self.modal:
       +            return
       +        self.hide()
       +        if self.collide_point(*touch.pos):
       +            return True
       +
       +    def show(self, pos, duration, width=None, modal=False):
       +        '''Animate the bubble into position'''
       +        self.modal = modal
       +        if width:
       +            self.width = width
       +        Window.add_widget(self)
       +        # wait for the bubble to adjust it's size according to text then animate
       +        Clock.schedule_once(lambda dt: self._show(pos, duration))
       +
       +    def _show(self, pos, duration):
       +
       +        def on_stop(*l):
       +            if duration:
       +                Clock.schedule_once(self.hide, duration + .5)
       +
       +        self.opacity = 0
       +        arrow_pos = self.arrow_pos
       +        if arrow_pos[0] in ('l', 'r'):
       +            pos = pos[0], pos[1] - (self.height/2)
       +        else:
       +            pos = pos[0] - (self.width/2), pos[1]
       +
       +        self.limit_to = Window
       +
       +        anim = Animation(opacity=1, pos=pos, d=.32)
       +        anim.bind(on_complete=on_stop)
       +        anim.cancel_all(self)
       +        anim.start(self)
       +
       +
       +    def hide(self, *dt):
       +        ''' Auto fade out the Bubble
       +        '''
       +        def on_stop(*l):
       +            Window.remove_widget(self)
       +        anim = Animation(opacity=0, d=.25)
       +        anim.bind(on_complete=on_stop)
       +        anim.cancel_all(self)
       +        anim.start(self)
       +
       +
       +class InfoContent(Widget):
       +    '''Abstract class to be used to add to content to InfoDialog'''
       +    pass
       +
       +
       +class InfoButton(Button):
       +    '''Button that is auto added to the dialog when setting `buttons:`
       +    property.
       +    '''
       +    pass
       +
       +
       +class EventsDialog(AnimatedPopup):
       +    ''' Abstract Popup that provides the following events
       +    .. events::
       +        `on_release`
       +        `on_press`
       +    '''
       +
       +    __events__ = ('on_release', 'on_press')
       +
       +    def __init__(self, **kwargs):
       +        super(EventsDialog, self).__init__(**kwargs)
       +        self._on_release = kwargs.get('on_release')
       +        Window.bind(size=self.on_size,
       +                    rotation=self.on_size)
       +        self.on_size(Window, Window.size)
       +
       +    def on_size(self, instance, value):
       +        if app.ui_mode[0] == 'p':
       +            self.size = Window.size
       +        else:
       +            #tablet
       +            if app.orientation[0] == 'p':
       +                #portrait
       +                self.size = Window.size[0]/1.67, Window.size[1]/1.4
       +            else:
       +                self.size = Window.size[0]/2.5, Window.size[1]
       +
       +    def on_release(self, instance):
       +        pass
       +
       +    def on_press(self, instance):
       +        pass
       +
       +    def close(self):
       +        self._on_release = None
       +        self.dismiss()
       +
       +
       +class InfoDialog(EventsDialog):
       +    ''' A dialog box meant to display info along with buttons at the bottom
       +
       +    '''
       +
       +    buttons = ListProperty([_('ok'), _('cancel')])
       +    '''List of Buttons to be displayed at the bottom'''
       +
       +    def __init__(self, **kwargs):
       +        self._old_buttons = self.buttons
       +        super(InfoDialog, self).__init__(**kwargs)
       +        self.on_buttons(self, self.buttons)
       +
       +    def on_buttons(self, instance, value):
       +        if 'buttons_layout' not in self.ids.keys():
       +            return
       +        if value == self._old_buttons:
       +            return
       +        blayout = self.ids.buttons_layout
       +        blayout.clear_widgets()
       +        for btn in value:
       +            ib = InfoButton(text=btn)
       +            ib.bind(on_press=partial(self.dispatch, 'on_press'))
       +            ib.bind(on_release=partial(self.dispatch, 'on_release'))
       +            blayout.add_widget(ib)
       +        self._old_buttons = value
       +        pass
       +
       +    def add_widget(self, widget, index=0):
       +        if isinstance(widget, InfoContent):
       +            self.ids.info_content.add_widget(widget, index=index)
       +        else:
       +            super(InfoDialog, self).add_widget(widget)
       +
       +
       +class TakeInputDialog(InfoDialog):
       +    ''' A simple Dialog for displaying a message and taking a input
       +    using a Textinput
       +    '''
       +
       +    text = StringProperty('Nothing set yet')
       +
       +    readonly = BooleanProperty(False)
       +
       +
       +class EditLabelDialog(TakeInputDialog):
       +    pass
       +
       +
       +
       +class ImportPrivateKeysDialog(TakeInputDialog):
       +    pass
       +
       +
       +
       +class ShowMasterPublicKeyDialog(TakeInputDialog):
       +    pass
       +
       +
       +class EditDescriptionDialog(TakeInputDialog):
       +
       +    pass
       +
       +
       +class PrivateKeyDialog(InfoDialog):
       +
       +    private_key = StringProperty('')
       +    ''' private key to be displayed in the TextInput
       +    '''
       +
       +    address = StringProperty('')
       +    ''' address to be displayed in the dialog
       +    '''
       +
       +
       +class SignVerifyDialog(InfoDialog):
       +
       +    address = StringProperty('')
       +    '''current address being verified'''
       +
       +
       +
       +class MessageBox(InfoDialog):
       +
       +    image = StringProperty('atlas://gui/kivy/theming/light/info')
       +    '''path to image to be displayed on the left'''
       +
       +    message = StringProperty('Empty Message')
       +    '''Message to be displayed on the dialog'''
       +
       +    def __init__(self, **kwargs):
       +        super(MessageBox, self).__init__(**kwargs)
       +        self.title = kwargs.get('title', _('Message'))
       +
       +
       +class MessageBoxExit(MessageBox):
       +
       +    def __init__(self, **kwargs):
       +        super(MessageBox, self).__init__(**kwargs)
       +        self.title = kwargs.get('title', _('Exiting'))
       +
       +class MessageBoxError(MessageBox):
       +
       +    def __init__(self, **kwargs):
       +        super(MessageBox, self).__init__(**kwargs)
       +        self.title = kwargs.get('title', _('Error'))
       +
       +
       +class WalletAddressesDialog(CarouselDialog):
       +
       +    def __init__(self, **kwargs):
       +        super(WalletAddressesDialog, self).__init__(**kwargs)
       +        CarouselHeader = Factory.CarouselHeader
       +        ch = CarouselHeader()
       +        ch.slide = 0 # idx
       +
       +        # delayed init
       +        global ScreenAddress
       +        if not ScreenAddress:
       +            from electrum_gui.kivy.screens import ScreenAddress
       +        slide = ScreenAddress()
       +
       +        slide.tab=ch
       +
       +        labels = app.wallet.labels
       +        addresses = app.wallet.addresses()
       +        _labels = {}
       +        for address in addresses:
       +            _labels[labels.get(address, address)] = address
       +
       +        slide.labels = _labels
       +
       +        self.add_widget(slide)
       +        self.add_widget(ch)
       +        Clock.schedule_once(lambda dt: self.delayed_init(slide))
       +
       +    def delayed_init(self, slide):
       +        # add a tab for each wallet
       +        # for wallet in wallets
       +        slide.ids.btn_address.values = values = slide.labels.keys()
       +        slide.ids.btn_address.text = values[0]
       +
       +
       +
       +class RecentActivityDialog(CarouselDialog):
       +
       +    def send_payment(self, address):
       +        tabs = app.root.main_screen.ids.tabs
       +        screen_send = tabs.ids.screen_send
       +        # remove self
       +        self.dismiss()
       +        # switch_to the send screen
       +        tabs.ids.panel.switch_to(tabs.ids.tab_send)
       +        # populate
       +        screen_send.ids.payto_e.text = address
       +
       +    def populate_inputs_outputs(self, app, tx_hash):
       +        if tx_hash:
       +            tx = app.wallet.transactions.get(tx_hash)
       +            self.ids.list_outputs.content_adapter.data = \
       +                [(address, app.gui.main_gui.format_amount(value))\
       +                for address, value in tx.outputs]
       +            self.ids.list_inputs.content_adapter.data = \
       +                [(input['address'], input['prevout_hash'])\
       +                for input in tx.inputs]
       +
       +
       +class CreateAccountDialog(EventsDialog):
       +    ''' Abstract dialog to be used as the base for all Create Account Dialogs
       +    '''
       +    crcontent = ObjectProperty(None)
       +
       +    def add_widget(self, widget, index=0):
       +        if not self.crcontent:
       +            super(CreateAccountDialog, self).add_widget(widget)
       +        else:
       +            self.crcontent.add_widget(widget, index=index)
       +
       +
       +class InitSeedDialog(CreateAccountDialog):
       +
       +    seed_msg = StringProperty('')
       +    '''Text to be displayed in the TextInput'''
       +
       +    message = StringProperty('')
       +    '''Message to be displayed under seed'''
       +
       +    seed = ObjectProperty(None)
       +
       +    def on_parent(self, instance, value):
       +        if value:
       +            stepper = self.ids.stepper
       +            stepper.opacity = 1
       +            stepper.source = 'atlas://gui/kivy/theming/light/stepper_full'
       +            self._back = _back = partial(self.ids.back.dispatch, 'on_release')
       +            app.navigation_higherarchy.append(_back)
       +
       +    def close(self):
       +        if self._back in app.navigation_higherarchy:
       +            app.navigation_higherarchy.pop()
       +            self._back = None
       +        super(InitSeedDialog, self).close()
       +
       +class CreateRestoreDialog(CreateAccountDialog):
       +    ''' Initial Dialog for creating or restoring seed'''
       +
       +    def on_parent(self, instance, value):
       +        if value:
       +            self.ids.but_close.disabled = True
       +            self.ids.but_close.opacity = 0
       +            self._back = _back = partial(app.dispatch, 'on_back')
       +            app.navigation_higherarchy.append(_back)
       +
       +    def close(self):
       +        if self._back in app.navigation_higherarchy:
       +            app.navigation_higherarchy.pop()
       +            self._back = None
       +        super(CreateRestoreDialog, self).close()
       +
       +
       +class VerifySeedDialog(CreateAccountDialog):
       +
       +    pass
       +
       +class RestoreSeedDialog(CreateAccountDialog):
       +
       +    pass
       +
       +class NewContactDialog(Popup):
       +
       +    qrscr = ObjectProperty(None)
       +    _decoder = None
       +
       +    def load_qr_scanner(self):
       +        global QRScanner
       +        if not QRScanner:
       +            from electrum_gui.kivy.qr_scanner import QRScanner
       +        qrscr = self.qrscr
       +        if not qrscr:
       +            self.qrscr = qrscr = QRScanner(opacity=0)
       +            #pos=self.pos, size=self.size)
       +            #self.bind(pos=qrscr.setter('pos'),
       +            #      size=qrscr.setter('size')
       +            qrscr.bind(symbols=self.on_symbols)
       +        bl = self.ids.bl
       +        bl.clear_widgets()
       +        bl.add_widget(qrscr)
       +        qrscr.opacity = 1
       +        Animation(height=dp(280)).start(self)
       +        Animation(opacity=1).start(self)
       +        qrscr.start()
       +
       +    def on_symbols(self, instance, value):
       +        instance.stop()
       +        self.remove_widget(instance)
       +        self.ids.but_contact.dispatch('on_release')
       +        global decode_uri
       +        if not decode_uri:
       +            from electrum_gui.kivy.qr_scanner import decode_uri
       +        uri = decode_uri(value[0].data)
       +        self.ids.ti.text = uri.get('address', 'empty')
       +        self.ids.ti_lbl.text = uri.get('label', 'empty')
       +        self.ids.ti_lbl.focus = True
       +
       +
       +class PasswordRequiredDialog(InfoDialog):
       +
       +    pass
       +
       +
       +class ChangePasswordDialog(CreateAccountDialog):
       +
       +    message = StringProperty(_('Empty Message'))
       +    '''Message to be displayed.'''
       +
       +    mode = OptionProperty('new', options=('new', 'confirm', 'create'))
       +    ''' Defines the mode of the password dialog.'''
       +
       +    def validate_new_password(self):
       +        self.ids.confirm.dispatch('on_release')
       +
       +    def on_parent(self, instance, value):
       +        if value:
       +            stepper = self.ids.stepper
       +            stepper.opacity = 1
       +            stepper.source = 'atlas://gui/kivy/theming/light/stepper_left'
       +            self._back = _back = partial(self.ids.back.dispatch, 'on_release')
       +            app.navigation_higherarchy.append(_back)
       +
       +    def close(self):
       +        ids = self.ids
       +        ids.ti_wallet_name.text = ""
       +        ids.ti_wallet_name.focus = False
       +        ids.ti_password.text = ""
       +        ids.ti_password.focus = False
       +        ids.ti_new_password.text = ""
       +        ids.ti_new_password.focus = False
       +        ids.ti_confirm_password.text = ""
       +        ids.ti_confirm_password.focus = False
       +        if self._back in app.navigation_higherarchy:
       +            app.navigation_higherarchy.pop()
       +            self._back = None
       +        super(ChangePasswordDialog, self).close()
       +
       +
       +
       +class Dialog(Popup):
       +
       +    content_padding = NumericProperty('2dp')
       +    '''Padding for the content area of the dialog defaults to 2dp
       +    '''
       +
       +    buttons_padding = NumericProperty('2dp')
       +    '''Padding for the bottns area of the dialog defaults to 2dp
       +    '''
       +
       +    buttons_height = NumericProperty('40dp')
       +    '''Height to be used for the Buttons at the bottom
       +    '''
       +
       +    def close(self):
       +        self.dismiss()
       +
       +    def add_content(self, widget, index=0):
       +        self.ids.layout_content.add_widget(widget, index)
       +
       +    def add_button(self, widget, index=0):
       +        self.ids.layout_buttons.add_widget(widget, index)
       +
       +
       +class SaveDialog(Popup):
       +
       +    filename = StringProperty('')
       +    '''The default file name provided
       +    '''
       +
       +    filters = ListProperty([])
       +    ''' list of files to be filtered and displayed defaults to  allow all
       +    '''
       +
       +    path = StringProperty(DEFAULT_PATH)
       +    '''path to be loaded by default in this dialog
       +    '''
       +
       +    file_chooser = ObjectProperty(None)
       +    '''link to the file chooser object inside the dialog
       +    '''
       +
       +    text_input = ObjectProperty(None)
       +    '''
       +    '''
       +
       +    cancel_button = ObjectProperty(None)
       +    '''
       +    '''
       +
       +    save_button = ObjectProperty(None)
       +    '''
       +    '''
       +
       +    def close(self):
       +        self.dismiss()
       +
       +
       +class LoadDialog(SaveDialog):
       +
       +    def _get_load_btn(self):
       +        return self.save_button
       +
       +    load_button = AliasProperty(_get_load_btn, None, bind=('save_button', ))
       +    '''Alias to the Save Button to be used as LoadButton
       +    '''
       +
       +    def __init__(self, **kwargs):
       +        super(LoadDialog, self).__init__(**kwargs)
       +        self.load_button.text=_("Load")
   DIR diff --git a/gui/kivy/drawer.py b/gui/kivy/drawer.py
       t@@ -0,0 +1,173 @@
       +
       +from kivy.uix.stencilview import StencilView
       +from kivy.uix.boxlayout import BoxLayout
       +from kivy.uix.image import Image
       +
       +from kivy.animation import Animation
       +from kivy.clock import Clock
       +from kivy.properties import OptionProperty, NumericProperty, ObjectProperty
       +
       +# delayed import
       +app = None
       +
       +
       +class Drawer(StencilView):
       +
       +    state = OptionProperty('closed',
       +                            options=('closed', 'open', 'opening', 'closing'))
       +    '''This indicates the current state the drawer is in.
       +
       +    :attr:`state` is a `OptionProperty` defaults to `closed`. Can be one of
       +    `closed`, `open`, `opening`, `closing`.
       +    '''
       +
       +    scroll_timeout = NumericProperty(200)
       +    '''Timeout allowed to trigger the :data:`scroll_distance`,
       +    in milliseconds. If the user has not moved :data:`scroll_distance`
       +    within the timeout, the scrolling will be disabled and the touch event
       +    will go to the children.
       +
       +    :data:`scroll_timeout` is a :class:`~kivy.properties.NumericProperty`
       +    and defaults to 200 (milliseconds)
       +    '''
       +
       +    scroll_distance = NumericProperty('4dp')
       +    '''Distance to move before scrolling the :class:`Drawer` in pixels.
       +    As soon as the distance has been traveled, the :class:`Drawer` will
       +    start to scroll, and no touch event will go to children.
       +    It is advisable that you base this value on the dpi of your target
       +    device's screen.
       +
       +    :data:`scroll_distance` is a :class:`~kivy.properties.NumericProperty`
       +    and defaults to 20dp.
       +    '''
       +
       +    drag_area = NumericProperty(.1)
       +    '''The percentage of area on the left edge that triggers the opening of
       +    the drawer. from 0-1
       +
       +    :attr:`drag_area` is a `NumericProperty` defaults to 2
       +    '''
       +
       +    _hidden_widget = ObjectProperty(None)
       +    _overlay_widget = ObjectProperty(None)
       +
       +    def __init__(self, **kwargs):
       +        super(Drawer, self).__init__(**kwargs)
       +        self.bind(pos=self._do_layout,
       +                    size=self._do_layout,
       +                    children=self._do_layout)
       +
       +    def _do_layout(self, instance, value):
       +        if not self._hidden_widget or not self._overlay_widget:
       +            return
       +        self._overlay_widget.height = self._hidden_widget.height =\
       +            self.height
       +
       +    def on_touch_down(self, touch):
       +        if self.disabled:
       +            return
       +
       +        global app
       +        if not app:
       +            from kivy.app import App
       +            app = App.get_running_app()
       +
       +        # skip on tablet mode
       +        if app.ui_mode[0] == 't':
       +            return super(Drawer, self).on_touch_down(touch)
       +
       +        touch.ud['send_touch_down'] = False
       +        drag_area = ((self.width * self.drag_area)
       +                    if self.state[0] == 'c' else
       +                    self._hidden_widget.width)
       +        if touch.x > drag_area:
       +            return super(Drawer, self).on_touch_down(touch)
       +        self._touch = touch
       +        Clock.schedule_once(self._change_touch_mode,
       +                            self.scroll_timeout/1000.)
       +        touch.ud['in_drag_area'] = True
       +        touch.ud['send_touch_down'] = True
       +        return
       +
       +    def on_touch_move(self, touch):
       +        global app
       +        if not app:
       +            from kivy.app import App
       +            app = App.get_running_app()
       +        # skip on tablet mode
       +        if app.ui_mode[0] == 't':
       +            return super(Drawer, self).on_touch_move(touch)
       +
       +        if not touch.ud.get('in_drag_area', None):
       +            return super(Drawer, self).on_touch_move(touch)
       +
       +        self._overlay_widget.x = min(self._hidden_widget.width,
       +                        max(self._overlay_widget.x + touch.dx*2, 0))
       +        if abs(touch.x - touch.ox) < self.scroll_distance:
       +            return
       +        touch.ud['send_touch_down'] = False
       +        Clock.unschedule(self._change_touch_mode)
       +        self._touch = None
       +        self.state = 'opening' if touch.dx > 0 else 'closing'
       +        touch.ox = touch.x
       +        return
       +
       +    def _change_touch_mode(self, *args):
       +        if not self._touch:
       +            return
       +        touch = self._touch
       +        touch.ud['in_drag_area'] = False
       +        touch.ud['send_touch_down'] = False
       +        self._touch = None
       +        super(Drawer, self).on_touch_down(touch)
       +        return
       +
       +    def on_touch_up(self, touch):
       +        # skip on tablet mode
       +        if app.ui_mode[0] == 't':
       +            return super(Drawer, self).on_touch_down(touch)
       +
       +        if touch.ud.get('send_touch_down', None):
       +            Clock.unschedule(self._change_touch_mode)
       +            Clock.schedule_once(
       +                lambda dt: super(Drawer, self).on_touch_down(touch), -1)
       +        if touch.ud.get('in_drag_area', None):
       +            touch.ud['in_drag_area'] = False
       +        Animation.cancel_all(self._overlay_widget)
       +        anim = Animation(x=self._hidden_widget.width
       +                            if self.state[0] == 'o' else 0,
       +                            d=.1, t='linear')
       +        anim.bind(on_complete = self._complete_drawer_animation)
       +        anim.start(self._overlay_widget)
       +        Clock.schedule_once(
       +            lambda dt: super(Drawer, self).on_touch_up(touch), 0)
       +
       +    def _complete_drawer_animation(self, *args):
       +        self.state = 'open' if self.state[0] == 'o' else 'closed'
       +
       +    def add_widget(self, widget, index=1):
       +        if not widget:
       +            return
       +        children = self.children
       +        len_children = len(children)
       +        if len_children == 2:
       +            Logger.debug('Drawer: No more than two widgets allowed')
       +            return
       +
       +        super(Drawer, self).add_widget(widget)
       +        if len_children == 0:
       +            # first widget add it to the hidden/drawer section
       +            self._hidden_widget = widget
       +            return
       +        # Second Widget
       +        self._overlay_widget = widget
       +
       +    def remove_widget(self, widget):
       +        super(Drawer, self).remove_widget(self)
       +        if widget == self._hidden_widget:
       +            self._hidden_widget = None
       +            return
       +        if widget == self._overlay_widget:
       +            self._overlay_widget = None
       +            return
       +\ No newline at end of file
   DIR diff --git a/gui/kivy/gridview.py b/gui/kivy/gridview.py
       t@@ -0,0 +1,203 @@
       +from kivy.uix.boxlayout import BoxLayout
       +from kivy.adapters.dictadapter import DictAdapter
       +from kivy.adapters.listadapter import ListAdapter
       +from kivy.properties import ObjectProperty, ListProperty, AliasProperty
       +from kivy.uix.listview import (ListItemButton, ListItemLabel, CompositeListItem,
       +                               ListView)
       +from kivy.lang import Builder
       +from kivy.metrics import dp, sp
       +
       +Builder.load_string('''
       +<GridView>
       +    header_view: header_view
       +    content_view: content_view
       +    BoxLayout:
       +        orientation: 'vertical'
       +        padding: '0dp', '2dp'
       +        BoxLayout:
       +            id: header_box
       +            orientation: 'vertical'
       +            size_hint: 1, None
       +            height: '30dp'
       +            ListView:
       +                id: header_view
       +        BoxLayout:
       +            id: content_box
       +            orientation: 'vertical'
       +            ListView:
       +                id: content_view
       +
       +<-HorizVertGrid>
       +    header_view: header_view
       +    content_view: content_view
       +    ScrollView:
       +        id: scrl
       +        do_scroll_y: False
       +        RelativeLayout:
       +            size_hint_x: None
       +            width: max(scrl.width, dp(sum(root.widths)))
       +            BoxLayout:
       +                orientation: 'vertical'
       +                padding: '0dp', '2dp'
       +                BoxLayout:
       +                    id: header_box
       +                    orientation: 'vertical'
       +                    size_hint: 1, None
       +                    height: '30dp'
       +                    ListView:
       +                        id: header_view
       +                BoxLayout:
       +                    id: content_box
       +                    orientation: 'vertical'
       +                    ListView:
       +                        id: content_view
       +
       +''')
       +
       +class GridView(BoxLayout):
       +    """Workaround solution for grid view by using 2 list view.
       +    Sometimes the height of lines is shown properly."""
       +
       +    def _get_hd_adpt(self):
       +        return self.ids.header_view.adapter
       +
       +    header_adapter = AliasProperty(_get_hd_adpt, None)
       +    '''
       +    '''
       +
       +    def _get_cnt_adpt(self):
       +        return self.ids.content_view.adapter
       +
       +    content_adapter = AliasProperty(_get_cnt_adpt, None)
       +    '''
       +    '''
       +
       +    headers = ListProperty([])
       +    '''
       +    '''
       +
       +    widths = ListProperty([])
       +    '''
       +    '''
       +
       +    data = ListProperty([])
       +    '''
       +    '''
       +
       +    getter = ObjectProperty(lambda item, i: item[i])
       +    '''
       +    '''
       +    on_context_menu = ObjectProperty(None)
       +
       +    def __init__(self, **kwargs):
       +        super(GridView, self).__init__(**kwargs)
       +        self._from_widths = False
       +        #self.on_headers(self, self.headers)
       +
       +    def on_widths(self, instance, value):
       +        self._from_widths = True
       +        self.on_headers(instance, self.headers)
       +        self._from_widths = False
       +
       +    def on_headers(self, instance, value):
       +        if not self._from_widths:
       +            return
       +        if not (value and self.canvas and self.headers):
       +            return
       +        widths = self.widths
       +        if len(self.widths) != len(value):
       +            return
       +        #if widths is not None:
       +        #    widths = ['%sdp' % i for i in widths]
       +
       +        def generic_args_converter(row_index,
       +                                   item,
       +                                   is_header=True,
       +                                   getter=self.getter):
       +            cls_dicts = []
       +            _widths = self.widths
       +            getter = self.getter
       +            on_context_menu = self.on_context_menu
       +
       +            for i, header in enumerate(self.headers):
       +                kwargs = {
       +                    'padding': ('2dp','2dp'),
       +                    'halign': 'center',
       +                    'valign': 'middle',
       +                    'size_hint_y': None,
       +                    'shorten': True,
       +                    'height': '30dp',
       +                    'text_size': (_widths[i], dp(30)),
       +                    'text': getter(item, i),
       +                }
       +
       +                kwargs['font_size'] = '9sp'
       +                if is_header:
       +                    kwargs['deselected_color'] = kwargs['selected_color']  =\
       +                        [0, 1, 1, 1]
       +                else:  # this is content
       +                    kwargs['deselected_color'] = 1, 1, 1, 1
       +                    if on_context_menu is not None:
       +                        kwargs['on_press'] = on_context_menu
       +
       +                if widths is not None:  # set width manually
       +                    kwargs['size_hint_x'] = None
       +                    kwargs['width'] = widths[i]
       +
       +                cls_dicts.append({
       +                    'cls': ListItemButton,
       +                    'kwargs': kwargs,
       +                })
       +
       +            return {
       +                'id': item[-1],
       +                'size_hint_y': None,
       +                'height': '30dp',
       +                'cls_dicts': cls_dicts,
       +            }
       +
       +        def header_args_converter(row_index, item):
       +            return generic_args_converter(row_index, item)
       +
       +        def content_args_converter(row_index, item):
       +            return generic_args_converter(row_index, item, is_header=False)
       +
       +
       +        self.ids.header_view.adapter = ListAdapter(data=[self.headers],
       +                                   args_converter=header_args_converter,
       +                                   selection_mode='single',
       +                                   allow_empty_selection=False,
       +                                   cls=CompositeListItem)
       +
       +        self.ids.content_view.adapter = ListAdapter(data=self.data,
       +                                   args_converter=content_args_converter,
       +                                   selection_mode='single',
       +                                   allow_empty_selection=False,
       +                                   cls=CompositeListItem)
       +        self.content_adapter.bind_triggers_to_view(self.ids.content_view._trigger_reset_populate)
       +
       +class HorizVertGrid(GridView):
       +    pass
       +
       +
       +if __name__ == "__main__":
       +    from kivy.app import App
       +    class MainApp(App):
       +
       +        def build(self):
       +            data = []
       +            for i in range(90):
       +                data.append((str(i), str(i)))
       +            self.data = data
       +            return Builder.load_string('''
       +BoxLayout:
       +    orientation: 'vertical'
       +    HorizVertGrid:
       +        on_parent: if args[1]: self.content_adapter.data = app.data
       +        headers:['Address', 'Previous output']
       +        widths: [400, 500]
       +
       +<Label>
       +    font_size: '16sp'
       +''')
       +    MainApp().run()
   DIR diff --git a/gui/kivy/installwizard.py b/gui/kivy/installwizard.py
       t@@ -0,0 +1,224 @@
       +from electrum import Wallet
       +from electrum.i18n import _
       +from electrum_gui.kivy.dialog import (CreateRestoreDialog, InitSeedDialog,
       +    ChangePasswordDialog)
       +
       +from kivy.app import App
       +from kivy.uix.widget import Widget
       +from kivy.core.window import Window
       +from kivy.clock import Clock
       +
       +#from seed_dialog import SeedDialog
       +#from network_dialog import NetworkDialog
       +#from util import *
       +#from amountedit import AmountEdit
       +
       +import sys
       +import threading
       +from functools import partial
       +
       +# global Variables
       +app = App.get_running_app()
       +
       +
       +class InstallWizard(Widget):
       +
       +    __events__ = ('on_wizard_complete', )
       +
       +    def __init__(self, config, network, storage):
       +        super(InstallWizard, self).__init__()
       +        self.config  = config
       +        self.network = network
       +        self.storage = storage
       +
       +    def waiting_dialog(self, task,
       +                       msg= _("Electrum is generating your addresses,"
       +                              " please wait.")):
       +        def target():
       +            task()
       +            Clock.schedule_once(lambda dt:
       +                app.show_info_bubble(text="Complete", duration=.5,
       +                    icon='atlas://gui/kivy/theming/light/important',
       +                    pos=Window.center, width='200dp', arrow_pos=None))
       +
       +        app.show_info_bubble(
       +            text=msg, icon='atlas://gui/kivy/theming/light/important',
       +            pos=Window.center, width='200sp', arrow_pos=None, modal=True)
       +        t = threading.Thread(target = target)
       +        t.start()
       +
       +    def run(self):
       +        CreateRestoreDialog(on_release=self.on_creatrestore_complete).open()
       +
       +    def on_creatrestore_complete(self, dialog, button):
       +        if not button:
       +            self.dispatch('on_wizard_complete', None)
       +            return
       +        wallet = Wallet(self.storage)
       +        gap = self.config.get('gap_limit', 5)
       +        if gap !=5:
       +            wallet.gap_limit = gap_limit
       +            wallet.storage.put('gap_limit', gap, True)
       +
       +        dialog.close()
       +        if button == dialog.ids.create:
       +            # create
       +            self.change_password_dialog(wallet=wallet)
       +        elif button == dialog.ids.restore:
       +            # restore
       +            wallet.init_seed(None)
       +            self.restore_seed_dialog()
       +        #elif button == dialog.ids.watching:
       +        #    self.action = 'watching'
       +        else:
       +            self.dispatch('on_wizard_complete', None)
       +
       +    def init_seed_dialog(self, wallet=None, instance=None, password=None,
       +                         wallet_name=None):
       +        # renamed from show_seed()
       +        '''Can be called directly (password is None)
       +        or from a password-protected callback (password is not None)'''
       +
       +        if not wallet or not wallet.seed:
       +            if instance == None:
       +                wallet.init_seed(None)
       +            else:
       +                return MessageBoxError(message=_('No seed')).open()
       +
       +        if password is None or not instance:
       +            seed = wallet.get_mnemonic(None)
       +        else:
       +            try:
       +                seed = self.wallet.get_seed(password)
       +            except Exception:
       +                return MessageBoxError(message=_('Incorrect Password'))
       +
       +        brainwallet = seed
       +
       +        msg2 = _("[color=#414141][b]"+\
       +                "[b]PLEASE WRITE DOWN YOUR SEED PASS[/b][/color]"+\
       +                "[size=9]\n\n[/size]" +\
       +                "[color=#929292]If you ever forget your pincode, your seed" +\
       +                " phrase will be the [color=#EB984E]"+\
       +                "[b]only way to recover[/b][/color] your wallet. Your " +\
       +                " [color=#EB984E][b]Bitcoins[/b][/color] will otherwise be" +\
       +                " [color=#EB984E]lost forever![/color]")
       +
       +        if wallet.imported_keys:
       +            msg2 += "[b][color=#ff0000ff]" + _("WARNING") + "[/color]:[/b] " +\
       +                    _("Your wallet contains imported keys. These keys cannot" +\
       +                    " be recovered from seed.")
       +
       +        def on_ok_press(_dlg, _btn):
       +            _dlg.close()
       +            if _btn != _dlg.ids.confirm:
       +                self.change_password_dialog(wallet)
       +                return
       +            if instance is None:
       +                # in initial phase
       +                def create(password):
       +                    try:
       +                        password = None if not password else password
       +                        wallet.save_seed(password)
       +                    except Exception as err:
       +                        Logger.Info('Wallet: {}'.format(err))
       +                        Clock.schedule_once(lambda dt:
       +                            app.show_error(err))
       +                    wallet.synchronize()  # generate first addresses offline
       +                self.waiting_dialog(partial(create, password))
       +
       +
       +        InitSeedDialog(message=msg2,
       +                        seed_msg=brainwallet,
       +                        seed=seed,
       +                        on_release=on_ok_press).open()
       +
       +    def change_password_dialog(self, wallet=None, instance=None):
       +        """Can be called directly (instance is None)
       +        or from a callback (instance is not None)"""
       +
       +        if instance and not wallet.seed:
       +            return MessageBoxExit(message=_('No seed !!')).open()
       +
       +        if instance is not None:
       +            if wallet.use_encryption:
       +                msg = (
       +                    _('Your wallet is encrypted. Use this dialog to change" + \
       +                    " your password.') + '\n' + _('To disable wallet" + \
       +                    " encryption, enter an empty new password.'))
       +                mode = 'confirm'
       +            else:
       +                msg = _('Your wallet keys are not encrypted')
       +                mode = 'new'
       +        else:
       +            msg = _("Please choose a password to encrypt your wallet keys.") +\
       +                '\n' + _("Leave these fields empty if you want to disable" + \
       +                " encryption.")
       +            mode = 'create'
       +
       +        def on_release(_dlg, _btn):
       +            ti_password = _dlg.ids.ti_password
       +            ti_new_password = _dlg.ids.ti_new_password
       +            ti_confirm_password = _dlg.ids.ti_confirm_password
       +            if _btn != _dlg.ids.next:
       +                _dlg.close()
       +                if not instance:
       +                    CreateRestoreDialog(
       +                        on_release=self.on_creatrestore_complete).open()
       +                return
       +
       +            # Confirm
       +            wallet_name = _dlg.ids.ti_wallet_name.text
       +            password = (unicode(ti_password.text)
       +                        if wallet.use_encryption else
       +                        None)
       +            new_password = unicode(ti_new_password.text)
       +            new_password2 = unicode(ti_confirm_password.text)
       +
       +            if new_password != new_password2:
       +                ti_password.text = ""
       +                ti_new_password.text = ""
       +                ti_confirm_password.text = ""
       +                if ti_password.disabled:
       +                    ti_new_password.focus = True
       +                else:
       +                    ti_password.focus = True
       +                return app.show_error(_('Passwords do not match'))
       +
       +            if not instance:
       +                _dlg.close()
       +                self.init_seed_dialog(password=new_password,
       +                                      wallet=wallet,
       +                                      wallet_name=wallet_name)
       +                return
       +
       +            try:
       +                seed = wallet.decode_seed(password)
       +            except BaseException:
       +                return MessageBoxError(
       +                    message=_('Incorrect Password')).open()
       +
       +            # test carefully
       +            try:
       +                wallet.update_password(seed, password, new_password)
       +            except BaseException:
       +                return MessageBoxExit(
       +                    message=_('Failed to update password')).open()
       +            else:
       +                app.show_info_bubble(
       +                    text=_('Password successfully updated'), duration=1,
       +                    pos=_btn.pos)
       +            _dlg.close()
       +
       +
       +            if instance is None:  # in initial phase
       +                self.load_wallet()
       +            self.app.gui.main_gui.update_wallet()
       +
       +        cpd = ChangePasswordDialog(
       +                             message=msg,
       +                             mode=mode,
       +                             on_release=on_release).open()
       +
       +    def on_wizard_complete(self, instance, wallet):
       +        pass
   DIR diff --git a/gui/kivy/main.kv b/gui/kivy/main.kv
       t@@ -0,0 +1,401 @@
       +#:import Window kivy.core.window.Window
       +#:import _ electrum.i18n._
       +#:import partial functools.partial
       +
       +# Custom Global Widgets
       +
       +<VGridLayout@GridLayout>:
       +    rows: 1
       +    size_hint: 1, None
       +    height: self.minimum_height
       +
       +<IconButton@ButtonBehavior+Image>
       +    allow_stretch: True
       +    size_hint_x: None
       +    width: self.height
       +    canvas:
       +        BorderImage:
       +            border: (10, 10, 10, 10)
       +            source:
       +                'atlas://gui/kivy/theming/light/' + ('tab_btn'\
       +                if root.state == 'normal' else 'icon_border')
       +            size: root.size
       +            pos: root.pos
       +###########################
       +##    Gloabal Defaults
       +###########################
       +
       +<Label>
       +    markup: True
       +    font_name: 'data/fonts/Roboto.ttf'
       +    font_size: '16sp'
       +
       +<ListItemButton>
       +    font_size: '12sp'
       +
       +#########################
       +#       Dialogs
       +#########################
       +
       +################################################
       +##      Create Dialogs
       +################################################
       +
       +<CreateAccountTextInput@TextInput>
       +    border: 4, 4, 4, 4
       +    font_size: '15sp'
       +    padding: '15dp', '15dp'
       +    background_color: (1, 1, 1, 1) if self.focus else (0.454, 0.698, 0.909, 1)
       +    foreground_color: (0.31, 0.31, 0.31, 1) if self.focus else (0.835, 0.909, 0.972, 1)
       +    hint_text_color: self.foreground_color
       +    background_active: 'atlas://gui/kivy/theming/light/create_act_text_active'
       +    background_normal: 'atlas://gui/kivy/theming/light/create_act_text_active'
       +    size_hint_y: None
       +    height: '48sp'
       +
       +<CreateAccountButtonBlue@Button>
       +    canvas.after:
       +        Color
       +            rgba: 1, 1, 1, 1 if self.disabled else 0
       +        Rectangle:
       +            texture: self.texture
       +            size: self.size
       +            pos: self.pos
       +        Color
       +            rgba: .5, .5, .5, .5 if self.disabled else 0
       +        Rectangle:
       +            texture: self.texture
       +            size: self.size
       +            pos: self.x - dp(1), self.y + dp(1)
       +    border: 15, 5, 5, 5
       +    background_color: (1, 1, 1, 1) if self.disabled else (.203, .490, .741, 1 if self.state == 'normal' else .75)
       +    size_hint: 1, None
       +    height: '48sp'
       +    text_size: self.size
       +    halign: 'center'
       +    valign: 'middle'
       +    background_normal: 'atlas://gui/kivy/theming/light/btn_create_account'
       +    background_down: 'atlas://gui/kivy/theming/light/btn_create_account'
       +    background_disabled_normal: 'atlas://gui/kivy/theming/light/btn_create_act_disabled'
       +    on_release: self.root.dispatch('on_press', self)
       +    on_release: self.root.dispatch('on_release', self)
       +
       +<CreateAccountButtonGreen@CreateAccountButtonBlue>
       +    background_color: (1, 1, 1, 1) if self.disabled else (.415, .717, 0, 1 if self.state == 'normal' else .75)
       +
       +<InfoBubble>
       +    canvas.before:
       +        Color:
       +            rgba: 0, 0, 0, .7 if root.dim_background else 0
       +        Rectangle:
       +            size: Window.size
       +    size_hint: None, None
       +    width: '270dp' if root.fs else min(self.width, dp(270))
       +    height: self.width if self.fs else (lbl.texture_size[1] + dp(27))
       +    on_touch_down: self.hide()
       +    BoxLayout:
       +        padding: '5dp'
       +        Widget:
       +            size_hint: None, 1
       +            width: '4dp' if root.fs else '2dp'
       +        Image:
       +            id: img
       +            source: root.icon
       +            mipmap: True
       +            size_hint: None, 1
       +            width: (root.width - dp(20)) if root.fs  else (0 if not root.icon else '32dp')
       +        Label:
       +            id: lbl
       +            markup: True
       +            font_size: '12sp'
       +            text: root.message
       +            text_size: self.width, None
       +            size_hint: None, 1
       +            width: 0 if root.fs else (root.width - img.width)
       +
       +<-CreateAccountDialog>
       +    text_color: .854, .925, .984, 1
       +    auto_dismiss: False
       +    size_hint: None, None
       +    canvas.before:
       +        Color:
       +            rgba: 0, 0, 0, .9
       +        Rectangle:
       +            size: Window.size
       +        Color:
       +            rgba: .239, .588, .882, 1
       +        Rectangle:
       +            size: Window.size
       +
       +    crcontent: crcontent
       +    # add electrum icon
       +    FloatLayout:
       +        size_hint: None, None
       +        size: 0, 0
       +        IconButton:
       +            id: but_close
       +            size_hint: None, None
       +            size: '27dp', '27dp'
       +            top: Window.height - dp(10)
       +            right: Window.width - dp(10)
       +            source: 'atlas://gui/kivy/theming/light/closebutton'
       +            on_release: root.dispatch('on_press', self)
       +            on_release: root.dispatch('on_release', self)
       +    BoxLayout:
       +        orientation: 'vertical' if self.width < self.height else 'horizontal'
       +        padding:
       +            min(dp(42), self.width/8), min(dp(60), self.height/9.7),\
       +            min(dp(42), self.width/8), min(dp(72), self.height/8)
       +        spacing: '27dp'
       +        GridLayout:
       +            id: grid_logo
       +            cols: 1
       +            pos_hint: {'center_y': .5}
       +            size_hint: 1, .62
       +            #height: self.minimum_height
       +            Image:
       +                id: logo_img
       +                mipmap: True
       +                allow_stretch: True
       +                size_hint: 1, None
       +                height: '110dp'
       +                source: 'atlas://gui/kivy/theming/light/electrum_icon640'
       +            Widget:
       +                size_hint: 1, None
       +                height: 0 if stepper.opacity else dp(15)
       +            Label:
       +                color: root.text_color
       +                opacity: 0 if stepper.opacity else 1
       +                text: 'ELECTRUM'
       +                size_hint: 1, None
       +                height: self.texture_size[1] if self.opacity else 0
       +                font_size: '33sp'
       +                font_name: 'data/fonts/tron/Tr2n.ttf'
       +            Image:
       +                id: stepper
       +                allow_stretch: True
       +                opacity: 0
       +                source: 'atlas://gui/kivy/theming/light/stepper_left'
       +                size_hint: 1, None
       +                height: grid_logo.height/2.5 if self.opacity else 0
       +        Widget:
       +            size_hint: 1, None
       +            height: '5dp'
       +        GridLayout:
       +            cols: 1
       +            id: crcontent
       +            spacing: '13dp'
       +
       +<CreateRestoreDialog>
       +    Label:
       +        color: root.text_color
       +        size_hint: 1, None
       +        text_size: self.width, None
       +        height: self.texture_size[1]
       +        text:
       +            _("Wallet file not found!!")+\
       +            "\n\n" + _("Do you want to create a new wallet ")+\
       +            _("or restore an existing one?")
       +    Widget
       +        size_hint: 1, None
       +        height: dp(15)
       +    GridLayout:
       +        id: grid
       +        orientation: 'vertical'
       +        cols: 1
       +        spacing: '14dp'
       +        size_hint: 1, None
       +        height: self.minimum_height
       +        CreateAccountButtonGreen:
       +            id: create
       +            text: _('Create a Wallet')
       +            root: root
       +        CreateAccountButtonBlue:
       +            id: restore
       +            text: _('I already have a wallet')
       +            root: root
       +        #CreateAccountButtonBlue:
       +        #    id: watching
       +        #    text: _('Create a Watching only wallet')
       +        #    root: root
       +
       +<InitSeedDialog>
       +    spacing: '12dp'
       +    GridLayout:
       +        id: grid
       +        cols: 1
       +        pos_hint: {'center_y': .5}
       +        size_hint_y: None
       +        height: dp(180)
       +        orientation: 'vertical'
       +        Button:
       +            border: 4, 4, 4, 4
       +            halign: 'justify'
       +            valign: 'middle'
       +            font_size: self.width/21
       +            text_size: self.width - dp(24), self.height - dp(12)
       +            #size_hint: 1, None
       +            #height: self.texture_size[1] + dp(24)
       +            background_normal: 'atlas://gui/kivy/theming/light/white_bg_round_top'
       +            background_down: self.background_normal
       +            text: root.message
       +        GridLayout:
       +            rows: 1
       +            size_hint: 1, .7
       +            #size_hint_y: None
       +            #height: but_seed.texture_size[1] + dp(24)
       +            Button:
       +                id: but_seed
       +                border: 4, 4, 4, 4
       +                halign: 'justify'
       +                valign: 'middle'
       +                font_size: self.width/15
       +                text: root.seed_msg
       +                text_size: self.width - dp(24), self.height - dp(12)
       +                background_normal: 'atlas://gui/kivy/theming/light/lightblue_bg_round_lb'
       +                background_down: self.background_normal
       +            Button:
       +                id: bt
       +                size_hint_x: .25
       +                background_normal: 'atlas://gui/kivy/theming/light/blue_bg_round_rb'
       +                background_down: self.background_normal
       +                Image:
       +                    mipmap: True
       +                    source: 'atlas://gui/kivy/theming/light/qrcode'
       +                    size: bt.size
       +                    center: bt.center
       +                 #on_release:
       +    GridLayout:
       +        rows: 1
       +        spacing: '12dp'
       +        size_hint: 1, None
       +        height: self.minimum_height
       +        CreateAccountButtonBlue:
       +            id: back
       +            text: _('Back')
       +            root: root
       +        CreateAccountButtonGreen:
       +            id: confirm
       +            text: _('Confirm')
       +            root: root
       +
       +<ChangePasswordDialog>
       +    padding: '7dp'
       +    CreateAccountTextInput:
       +        id: ti_wallet_name
       +        hint_text: 'Your Wallet Name'
       +        multiline: False
       +        on_text_validate:
       +            next = ti_new_password if ti_password.disabled else ti_password
       +            next.focus = True
       +    CreateAccountTextInput:
       +        id: ti_password
       +        hint_text: 'Enter old pincode'
       +        size_hint_y: None
       +        height: 0 if self.disabled else '38sp'
       +        password: True
       +        disabled: True if root.mode in ('new', 'create') else False
       +        opacity: 0 if self.disabled else 1
       +        multiline: False
       +        on_text_validate:
       +            #root.validate_old_password()
       +            ti_new_password.focus = True
       +    CreateAccountTextInput:
       +        id: ti_new_password
       +        hint_text: 'Enter new pincode'
       +        multiline: False
       +        password: True
       +        on_text_validate: ti_confirm_password.focus = True
       +    CreateAccountTextInput:
       +        id: ti_confirm_password
       +        hint_text: 'Confirm pincode'
       +        password: True
       +        multiline: False
       +        on_text_validate: root.validate_new_passowrd()
       +    Widget
       +    GridLayout:
       +        rows: 1
       +        spacing: '12dp'
       +        size_hint: 1, None
       +        height: self.minimum_height
       +        CreateAccountButtonBlue:
       +            id: back
       +            text: _('Back')
       +            root: root
       +        CreateAccountButtonGreen:
       +            id: next
       +            text: _('Next')
       +            root: root
       +
       +###############################################
       +##              Wallet Management
       +###############################################
       +
       +<WalletManagement@ScrollView>
       +    canvas.before:
       +        Color:
       +            rgba: .145, .145, .145, 1
       +        Rectangle:
       +            size: root.size
       +            pos: root.pos
       +    VGridLayout:
       +        Wallets:
       +            id: wallets_section
       +        Plugins:
       +            id: plugins_section
       +        Commands:
       +            id: commands_section
       +
       +<WalletManagementItem@BoxLayout>
       +
       +<Header@WalletManagementItem>
       +
       +<Wallets@VGridLayout>
       +    Header
       +
       +<Plugins@VGridLayout>
       +    Header
       +
       +<Commands@VGridLayout>
       +    Header
       +
       +################################################
       +##      This is our Root Widget of the app
       +################################################
       +StencilView
       +    manager: manager
       +    Drawer
       +        id: drawer
       +        size: root.size
       +        WalletManagement
       +            id: wallet_management
       +            canvas.before:
       +                Color:
       +                    rgba: .176, .176, .176, 1
       +                Rectangle:
       +                    size: self.size
       +                    pos: self.pos
       +            canvas.after:
       +                Color
       +                    rgba: 1, 1, 1, 1
       +                BorderImage
       +                    border: 0, 32, 0, 0
       +                    source: 'atlas://gui/kivy/theming/light/shadow_right'
       +                    pos: self.pos
       +                    size: self.size
       +            width:
       +                (root.width * .877) if app.ui_mode[0] == 'p'\
       +                else root.width * .35 if app.orientation[0] == 'l'\
       +                else root.width * .10
       +            height: root.height
       +        ScreenManager:
       +            id: manager
       +            x: wallet_management.width if app.ui_mode[0] == 't' else 0
       +            size: root.size
       +            canvas.before:
       +                Color
       +                    rgba: 1, 1, 1, 1
       +                BorderImage:
       +                    border: 2, 2, 2, 23
       +                    size: self.size
       +                    pos: self.x, self.y
       +\ No newline at end of file
   DIR diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py
       t@@ -0,0 +1,294 @@
       +import  sys
       +
       +from electrum import WalletStorage, Wallet
       +from electrum.i18n import _
       +
       +from kivy.app import App
       +from kivy.core.window import Window
       +from kivy.metrics import inch
       +from kivy.logger import Logger
       +from kivy.utils import platform
       +from kivy.properties import (OptionProperty, AliasProperty, ObjectProperty,
       +                             StringProperty, ListProperty)
       +
       +#inclusions for factory so that widgets can be used in kv
       +from gui.kivy.drawer import Drawer
       +from gui.kivy.dialog import InfoBubble
       +
       +class ElectrumWindow(App):
       +
       +    title = _('Electrum App')
       +
       +    wallet = ObjectProperty(None)
       +    '''Holds the electrum wallet
       +
       +    :attr:`wallet` is a `ObjectProperty` defaults to None.
       +    '''
       +
       +    conf = ObjectProperty(None)
       +    '''Holds the electrum config
       +
       +    :attr:`conf` is a `ObjectProperty`, defaults to None.
       +    '''
       +
       +    status = StringProperty(_('Uninitialised'))
       +    '''The status of the connection should show the balance when connected
       +
       +    :attr:`status` is a `StringProperty` defaults to _'uninitialised'
       +    '''
       +
       +    base_unit = StringProperty('BTC')
       +    '''BTC or UBTC or ...
       +
       +    :attr:`base_unit` is a `StringProperty` defaults to 'BTC'
       +    '''
       +
       +    _ui_mode = OptionProperty('phone', options=('tablet', 'phone'))
       +
       +    def _get_ui_mode(self):
       +        return self._ui_mode
       +
       +    ui_mode = AliasProperty(_get_ui_mode,
       +                            None,
       +                            bind=('_ui_mode',))
       +    '''Defines tries to ascertain the kind of device the app is running on.
       +    Cane be one of `tablet` or `phone`.
       +
       +    :data:`ui_mode` is a read only `AliasProperty` Defaults to 'phone'
       +    '''
       +
       +    _orientation = OptionProperty('landscape',
       +                                 options=('landscape', 'portrait'))
       +
       +    def _get_orientation(self):
       +        return self._orientation
       +
       +    orientation = AliasProperty(_get_orientation,
       +                                None,
       +                                bind=('_orientation',))
       +    '''Tries to ascertain the kind of device the app is running on.
       +    Cane be one of `tablet` or `phone`.
       +
       +    :data:`orientation` is a read only `AliasProperty` Defaults to 'landscape'
       +    '''
       +
       +    navigation_higherarchy = ListProperty([])
       +    '''This is a list of the current navigation higherarchy of the app used to
       +    navigate using back button.
       +
       +    :attr:`navigation_higherarchy` is s `ListProperty` defaults to []
       +    '''
       +
       +    __events__ = ('on_back', )
       +
       +    def __init__(self, **kwargs):
       +        # initialize variables
       +        self.info_bubble = None
       +        super(ElectrumWindow, self).__init__(**kwargs)
       +        self.network = network = kwargs.get('network')
       +        self.electrum_config = config = kwargs.get('config')
       +
       +    def load_wallet(self, wallet):
       +        # TODO
       +        pass
       +
       +    def build(self):
       +        from kivy.lang import Builder
       +        return Builder.load_file('gui/kivy/main.kv')
       +
       +    def _pause(self):
       +        if platform == 'android':
       +            from jnius import autoclass
       +            python_act = autoclass('org.renpy.android.PythonActivity')
       +            mActivity = python_act.mActivity
       +            mActivity.moveTaskToBack(True)
       +
       +    def on_start(self):
       +        Window.bind(size=self.on_size,
       +                    on_keyboard=self.on_keyboard)
       +        Window.bind(keyboard_height=self.on_keyboard_height)
       +        self.on_size(Window, Window.size)
       +        config = self.electrum_config
       +        storage = WalletStorage(config)
       +
       +        Logger.info('Electrum: Check for existing wallet')
       +        if not storage.file_exists:
       +            # start installation wizard
       +            Logger.debug('Electrum: Wallet not found. Launching install wizard')
       +            import installwizard
       +            wizard = installwizard.InstallWizard(config, self.network,
       +                                                 storage)
       +            wizard.bind(on_wizard_complete=self.on_wizard_complete)
       +            wizard.run()
       +        else:
       +            wallet = Wallet(storage)
       +            wallet.start_threads(self.network)
       +            self.on_wizard_complete(None, wallet)
       +
       +        self.on_resume()
       +
       +    def on_back(self):
       +        ''' Manage screen higherarchy
       +        '''
       +        try:
       +            self.navigation_higherarchy.pop()()
       +        except IndexError:
       +            # capture back button and pause app.
       +            self._pause()
       +
       +    def on_keyboard_height(self, *l):
       +        from kivy.animation import Animation
       +        from kivy.uix.popup import Popup
       +        active_widg = Window.children[0]
       +        active_widg = active_widg\
       +            if (active_widg == self.root or\
       +            issubclass(active_widg.__class__, Popup)) else\
       +            Window.children[1]
       +        Animation(y=Window.keyboard_height, d=.1).start(active_widg)
       +
       +    def on_keyboard(self, instance, key, keycode, codepoint, modifiers):
       +        # override settings button
       +        if key in (319, 282):
       +            self.gui.main_gui.toggle_settings(self)
       +            return True
       +        if key == 27:
       +            self.dispatch('on_back')
       +            return True
       +
       +    def on_wizard_complete(self, instance, wallet):
       +        if not wallet:
       +            Logger.debug('Electrum: No Wallet set/found. Exiting...')
       +            self.stop()
       +            sys.exit()
       +        return
       +
       +        # plugins that need to change the GUI do it here
       +        #run_hook('init')
       +
       +        self.load_wallet(wallet)
       +
       +        Clock.schedule_once(update_wallet)
       +
       +        #self.windows.append(w)
       +        #if url: w.set_url(url)
       +        #w.app = self.app
       +        #w.connect_slots(s)
       +        #w.update_wallet()
       +
       +        #self.app.exec_()
       +
       +        wallet.stop_threads()
       +
       +    def on_pause(self):
       +        '''
       +        '''
       +        # pause nfc
       +        # pause qrscanner(Camera) if active
       +        return True
       +
       +    def on_resume(self):
       +        '''
       +        '''
       +        # resume nfc
       +        # resume camera if active
       +        pass
       +
       +    def on_size(self, instance, value):
       +        width, height = value
       +        self._orientation = 'landscape' if width > height else 'portrait'
       +        self._ui_mode = 'tablet' if min(width, height) > inch(3.51) else 'phone'
       +        Logger.debug('orientation: {} ui_mode: {}'.format(self._orientation,
       +                                                          self._ui_mode))
       +
       +    def load_screen(self, index=0, direction='left'):
       +        '''
       +        '''
       +        screen = Builder.load_file('data/screens/' + self.screens[index])
       +        screen.name = self.screens[index]
       +        root.manager.switch_to(screen, direction=direction)
       +
       +    def load_next_screen(self):
       +        '''
       +        '''
       +        manager = root.manager
       +        try:
       +            self.load_screen(self.screens.index(manager.current_screen.name)+1)
       +        except IndexError:
       +            self.load_screen()
       +
       +    def load_previous_screen(self):
       +        '''
       +        '''
       +        manager = root.manager
       +        try:
       +            self.load_screen(self.screens.index(manager.current_screen.name)-1,
       +                             direction='right')
       +        except IndexError:
       +            self.load_screen(-1, direction='right')
       +
       +    def show_error(self, error,
       +                   width='200dp',
       +                   pos=None,
       +                   arrow_pos=None):
       +        ''' Show a error Message Bubble.
       +        '''
       +        self.show_info_bubble(
       +                    text=error,
       +                    icon='atlas://gui/kivy/theming/light/error',
       +                    width=width,
       +                    pos=pos or Window.center,
       +                    arrow_pos=arrow_pos)
       +
       +    def show_info_bubble(self,
       +                    text=_('Hello World'),
       +                    pos=(0, 0),
       +                    duration=0,
       +                    arrow_pos='bottom_mid',
       +                    width=None,
       +                    icon='',
       +                    modal=False):
       +        '''Method to show a Information Bubble
       +
       +        .. parameters::
       +            text: Message to be displayed
       +            pos: position for the bubble
       +            duration: duration the bubble remains on screen. 0 = click to hide
       +            width: width of the Bubble
       +            arrow_pos: arrow position for the bubble
       +        '''
       +
       +        info_bubble = self.info_bubble
       +        if not info_bubble:
       +            info_bubble = self.info_bubble = InfoBubble()
       +
       +        if info_bubble.parent:
       +            info_bubble.hide()
       +            return
       +
       +        if not arrow_pos:
       +            info_bubble.show_arrow = False
       +        else:
       +            info_bubble.show_arrow = True
       +            info_bubble.arrow_pos = arrow_pos
       +        img = info_bubble.ids.img
       +        if text == 'texture':
       +            # icon holds a texture not a source image
       +            # display the texture in full screen
       +            text = ''
       +            img.texture = icon
       +            info_bubble.fs = True
       +            info_bubble.show_arrow = False
       +            img.allow_stretch = True
       +            info_bubble.dim_background = True
       +            pos = (Window.center[0], Window.center[1] - info_bubble.center[1])
       +            info_bubble.background_image = 'atlas://gui/kivy/theming/light/card'
       +        else:
       +            info_bubble.fs = False
       +            info_bubble.icon = icon
       +            if img.texture and img._coreimage:
       +                img.reload()
       +            img.allow_stretch = False
       +            info_bubble.dim_background = False
       +            info_bubble.background_image = 'atlas://data/images/defaulttheme/bubble'
       +        info_bubble.message = text
       +        info_bubble.show(pos, duration, width, modal=modal)
   DIR diff --git a/gui/kivy/menus.py b/gui/kivy/menus.py
       t@@ -0,0 +1,95 @@
       +from functools import partial
       +
       +from kivy.animation import Animation
       +from kivy.core.window import Window
       +from kivy.clock import Clock
       +from kivy.uix.bubble import Bubble, BubbleButton
       +from kivy.properties import ListProperty
       +from kivy.uix.widget import Widget
       +
       +from electrum_gui.i18n import _
       +
       +class ContextMenuItem(Widget):
       +    '''abstract class
       +    '''
       +
       +class ContextButton(ContextMenuItem, BubbleButton):
       +    pass
       +
       +class ContextMenu(Bubble):
       +
       +    buttons = ListProperty([_('ok'), _('cancel')])
       +    '''List of Buttons to be displayed at the bottom'''
       +
       +    __events__ = ('on_press', 'on_release')
       +
       +    def __init__(self, **kwargs):
       +        self._old_buttons = self.buttons
       +        super(ContextMenu, self).__init__(**kwargs)
       +        self.on_buttons(self, self.buttons)
       +
       +    def on_touch_down(self, touch):
       +        if not self.collide_point(*touch.pos):
       +            self.hide()
       +            return
       +        return super(ContextMenu, self).on_touch_down(touch)
       +
       +    def on_buttons(self, _menu, value):
       +        if 'menu_content' not in self.ids.keys():
       +            return
       +        if value == self._old_buttons:
       +            return
       +        blayout = self.ids.menu_content
       +        blayout.clear_widgets()
       +        for btn in value:
       +            ib = ContextButton(text=btn)
       +            ib.bind(on_press=partial(self.dispatch, 'on_press'))
       +            ib.bind(on_release=partial(self.dispatch, 'on_release'))
       +            blayout.add_widget(ib)
       +        self._old_buttons = value
       +
       +    def on_press(self, instance):
       +        pass
       +
       +    def on_release(self, instance):
       +        pass
       +
       +    def show(self, pos, duration=0):
       +        Window.add_widget(self)
       +        # wait for the bubble to adjust it's size according to text then animate
       +        Clock.schedule_once(lambda dt: self._show(pos, duration))
       +
       +    def _show(self, pos, duration):
       +        def on_stop(*l):
       +            if duration:
       +                Clock.schedule_once(self.hide, duration + .5)
       +
       +        self.opacity = 0
       +        arrow_pos = self.arrow_pos
       +        if arrow_pos[0] in ('l', 'r'):
       +            pos = pos[0], pos[1] - (self.height/2)
       +        else:
       +            pos = pos[0] - (self.width/2), pos[1]
       +
       +        self.limit_to = Window
       +
       +        anim = Animation(opacity=1, pos=pos, d=.32)
       +        anim.bind(on_complete=on_stop)
       +        anim.cancel_all(self)
       +        anim.start(self)
       +
       +
       +    def hide(self, *dt):
       +
       +        def on_stop(*l):
       +            Window.remove_widget(self)
       +        anim = Animation(opacity=0, d=.25)
       +        anim.bind(on_complete=on_stop)
       +        anim.cancel_all(self)
       +        anim.start(self)
       +
       +    def add_widget(self, widget, index=0):
       +        if not isinstance(widget, ContextMenuItem):
       +            super(ContextMenu, self).add_widget(widget, index)
       +            return
       +        menu_content.add_widget(widget, index)
   DIR diff --git a/gui/kivy/nfc_scanner/__init__.py b/gui/kivy/nfc_scanner/__init__.py
       t@@ -0,0 +1,43 @@
       +'''
       +'''
       +from kivy.core import core_select_lib
       +from kivy.uix.widget import Widget
       +from kivy.properties import ObjectProperty
       +from kivy.factory import Factory
       +
       +__all__ = ('NFCBase', 'NFCScanner')
       +
       +class NFCBase(Widget):
       +
       +    payload = ObjectProperty(None)
       +
       +    def nfc_init(self):
       +        ''' Initialize the adapter
       +        '''
       +        pass
       +
       +    def nfc_disable(self):
       +        ''' Disable scanning
       +        '''
       +        pass
       +
       +    def nfc_enable(self):
       +        ''' Enable Scanning
       +        '''
       +        pass
       +
       +    def nfc_enable_exchange(self, data):
       +        ''' Start sending data
       +        '''
       +        pass
       +
       +    def nfc_disable_exchange(self):
       +        ''' Disable/Stop ndef exchange
       +        '''
       +        pass
       +
       +# load NFCScanner implementation
       +
       +NFCScanner = core_select_lib('nfc_scanner', (
       +    ('android', 'scanner_android', 'ScannerAndroid'),
       +    ('dummy', 'scanner_dummy', 'ScannerDummy')), True, 'electrum_gui.kivy')
   DIR diff --git a/gui/kivy/nfc_scanner/scanner_android.py b/gui/kivy/nfc_scanner/scanner_android.py
       t@@ -0,0 +1,86 @@
       +from kivy.utils import platform
       +if platform != 'android':
       +    raise ImportError
       +
       +from electrum_gui.kivy.nfc_scanner import NFCBase
       +from jnius import autoclass, cast
       +from android.runnable import run_on_ui_thread
       +from android import activity
       +
       +NfcAdapter = autoclass('android.nfc.NfcAdapter')
       +PythonActivity = autoclass('org.renpy.android.PythonActivity')
       +Intent = autoclass('android.content.Intent')
       +IntentFilter = autoclass('android.content.IntentFilter')
       +PendingIntent = autoclass('android.app.PendingIntent')
       +NdefRecord = autoclass('android.nfc.NdefRecord')
       +NdefMessage = autoclass('android.nfc.NdefMessage')
       +
       +class ScannerAndroid(NFCBase):
       +
       +    def nfc_init(self):
       +        # print 'nfc_init()'
       +
       +        # print 'configure nfc'
       +        self.j_context = context = PythonActivity.mActivity
       +        self.nfc_adapter = NfcAdapter.getDefaultAdapter(context)
       +        self.nfc_pending_intent = PendingIntent.getActivity(context, 0,
       +            Intent(context, context.getClass()).addFlags(
       +                Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
       +
       +        # print 'p2p filter'
       +        self.ndef_detected = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)
       +        self.ndef_detected.addDataType('text/plain')
       +        self.ndef_exchange_filters = [self.ndef_detected]
       +
       +    def on_new_intent(self, intent):
       +        # print 'on_new_intent()', intent.getAction()
       +        if intent.getAction() != NfcAdapter.ACTION_NDEF_DISCOVERED:
       +            # print 'unknow action, avoid.'
       +            return
       +
       +        rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
       +        # print 'raw messages', rawmsgs
       +        if not rawmsgs:
       +            return
       +
       +        for message in rawmsgs:
       +            message = cast(NdefMessage, message)
       +            # print 'got message', message
       +            payload = message.getRecords()[0].getPayload()
       +            self.payload = payload
       +            print 'payload: {}'.format(''.join(map(chr, payload)))
       +
       +    def nfc_disable(self):
       +        # print 'nfc_enable()'
       +        activity.bind(on_new_intent=self.on_new_intent)
       +
       +    def nfc_enable(self):
       +        # print 'nfc_enable()'
       +        activity.bind(on_new_intent=self.on_new_intent)
       +
       +    @run_on_ui_thread
       +    def _nfc_enable_ndef_exchange(self, data):
       +        # print 'create record'
       +        ndef_record = NdefRecord(
       +                NdefRecord.TNF_MIME_MEDIA,
       +                'text/plain', '', data)
       +        # print 'create message'
       +        ndef_message = NdefMessage([ndef_record])
       +
       +        # print 'enable ndef push'
       +        self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message)
       +
       +        # print 'enable dispatch', self.j_context, self.nfc_pending_intent
       +        self.nfc_adapter.enableForegroundDispatch(self.j_context,
       +                self.nfc_pending_intent, self.ndef_exchange_filters, [])
       +
       +    @run_on_ui_thread
       +    def _nfc_disable_ndef_exchange(self):
       +        self.nfc_adapter.disableForegroundNdefPush(self.j_context)
       +        self.nfc_adapter.disableForegroundDispatch(self.j_context)
       +
       +    def nfc_enable_exchange(self, data):
       +        self._nfc_enable_ndef_exchange()
       +
       +    def nfc_disable_exchange(self):
       +        self._nfc_disable_ndef_exchange()
   DIR diff --git a/gui/kivy/nfc_scanner/scanner_dummy.py b/gui/kivy/nfc_scanner/scanner_dummy.py
       t@@ -0,0 +1,37 @@
       +''' Dummy NFC Provider to be used on desktops in case no other provider is found
       +'''
       +from electrum_gui.kivy.nfc_scanner import NFCBase
       +from kivy.clock import Clock
       +from kivy.logger import Logger
       +
       +class ScannerDummy(NFCBase):
       +
       +    _initialised = False
       +
       +    def nfc_init(self):
       +        # print 'nfc_init()'
       +
       +        Logger.debug('NFC: configure nfc')
       +        self._initialised = True
       +
       +    def on_new_intent(self, dt):
       +        Logger.debug('NFC: got new dummy tag')
       +
       +    def nfc_enable(self):
       +        Logger.debug('NFC: enable')
       +        if self._initialised:
       +            Clock.schedule_interval(self.on_new_intent, 22)
       +
       +    def nfc_disable(self):
       +        # print 'nfc_enable()'
       +        Clock.unschedule(self.on_new_intent)
       +
       +    def nfc_enable_exchange(self, data):
       +        ''' Start sending data
       +        '''
       +        Logger.debug('NFC: sending data {}'.format(data))
       +
       +    def nfc_disable_exchange(self):
       +        ''' Disable/Stop ndef exchange
       +        '''
       +        Logger.debug('NFC: disable nfc exchange')
   DIR diff --git a/gui/kivy/qr_scanner/__init__.py b/gui/kivy/qr_scanner/__init__.py
       t@@ -0,0 +1,105 @@
       +'''QrScanner Base Abstract implementation
       +'''
       +
       +__all__ = ('ScannerBase', 'QRScanner')
       +
       +from collections import namedtuple
       +
       +from kivy.uix.anchorlayout import AnchorLayout
       +from kivy.core import core_select_lib
       +from kivy.properties import ListProperty, BooleanProperty
       +from kivy.factory import Factory
       +
       +
       +def encode_uri(addr, amount=0, label='', message='', size='',
       +            currency='btc'):
       +    ''' Convert to BIP0021 compatible URI
       +    '''
       +    uri = 'bitcoin:{}'.format(addr)
       +    first = True
       +    if amount:
       +        uri += '{}amount={}'.format('?' if first else '&', amount)
       +        first = False
       +    if label:
       +        uri += '{}label={}'.format('?' if first else '&', label)
       +        first = False
       +    if message:
       +        uri += '{}?message={}'.format('?' if first else '&', message)
       +        first = False
       +    if size:
       +        uri += '{}size={}'.format('?' if not first else '&', size)
       +    return uri
       +
       +def decode_uri(uri):
       +    if ':' not in uri:
       +        # It's just an address (not BIP21)
       +        return {'address': uri}
       +
       +    if '//' not in uri:
       +        # Workaround for urlparse, it don't handle bitcoin: URI properly
       +        uri = uri.replace(':', '://')
       +
       +    try:
       +        uri = urlparse(uri)
       +    except NameError:
       +        # delayed import
       +        from urlparse import urlparse, parse_qs
       +        uri = urlparse(uri)
       +
       +    result = {'address': uri.netloc} 
       +
       +    if uri.path.startswith('?'):
       +        params = parse_qs(uri.path[1:])
       +    else:
       +        params = parse_qs(uri.path)
       +
       +    for k,v in params.items():
       +        if k in ('amount', 'label', 'message', 'size'):
       +            result[k] = v[0]
       +
       +    return result
       +
       +
       +class ScannerBase(AnchorLayout):
       +    ''' Base implementation for camera based scanner
       +    '''
       +    camera_size = ListProperty([320, 240])
       +
       +    symbols = ListProperty([])
       +
       +    # XXX can't work now, due to overlay.
       +    show_bounds = BooleanProperty(False)
       +
       +    Qrcode = namedtuple('Qrcode',
       +            ['type', 'data', 'bounds', 'quality', 'count'])
       +
       +    def start(self):
       +        pass
       +
       +    def stop(self):
       +        pass
       +
       +    def on_symbols(self, instance, value):
       +        #if self.show_bounds:
       +        #    self.update_bounds()
       +        pass
       +
       +    def update_bounds(self):
       +        self.canvas.after.remove_group('bounds')
       +        if not self.symbols:
       +            return
       +        with self.canvas.after:
       +            Color(1, 0, 0, group='bounds')
       +            for symbol in self.symbols:
       +                x, y, w, h = symbol.bounds
       +                x = self._camera.right - x - w
       +                y = self._camera.top - y - h
       +                Line(rectangle=[x, y, w, h], group='bounds')
       +
       +
       +# load QRCodeDetector implementation
       +
       +QRScanner = core_select_lib('qr_scanner', (
       +    ('android', 'scanner_android', 'ScannerAndroid'),
       +    ('camera', 'scanner_camera', 'ScannerCamera')), False, 'electrum_gui.kivy')
       +Factory.register('QRScanner', cls=QRScanner)
   DIR diff --git a/gui/kivy/qr_scanner/scanner_android.py b/gui/kivy/qr_scanner/scanner_android.py
       t@@ -0,0 +1,354 @@
       +'''
       +Qrcode example application
       +==========================
       +
       +Author: Mathieu Virbel <mat@meltingrocks.com>
       +
       +License:
       +Copyright (c) 2013 Mathieu Virbel <mat@meltingrocks.com>
       +
       +Permission is hereby granted, free of charge, to any person obtaining a copy
       +of this software and associated documentation files (the "Software"), to deal
       +in the Software without restriction, including without limitation the rights
       +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       +copies of the Software, and to permit persons to whom the Software is
       +furnished to do so, subject to the following conditions:
       +
       +The above copyright notice and this permission notice shall be included in
       +all copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       +THE SOFTWARE.
       +
       +Featuring:
       +
       +- Android camera initialization
       +- Show the android camera into a Android surface that act as an overlay
       +- New AndroidWidgetHolder that control any android view as an overlay
       +- New ZbarQrcodeDetector that use AndroidCamera / PreviewFrame + zbar to
       +  detect Qrcode.
       +
       +'''
       +
       +__all__ = ('ScannerAndroid', )
       +
       +from kivy.utils import platform
       +if platform != 'android':
       +    raise ImportError
       +
       +from electrum_gui.kivy.qr_scanner import ScannerBase
       +from kivy.properties import ObjectProperty, NumericProperty
       +from kivy.uix.widget import Widget
       +from kivy.uix.anchorlayout import AnchorLayout
       +from kivy.graphics import Color, Line
       +from jnius import autoclass, PythonJavaClass, java_method, cast
       +from android.runnable import run_on_ui_thread
       +
       +# preload java classes
       +System = autoclass('java.lang.System')
       +System.loadLibrary('iconv')
       +PythonActivity = autoclass('org.renpy.android.PythonActivity')
       +Camera = autoclass('android.hardware.Camera')
       +ImageScanner = autoclass('net.sourceforge.zbar.ImageScanner')
       +Image = autoclass('net.sourceforge.zbar.Image')
       +Symbol = autoclass('net.sourceforge.zbar.Symbol')
       +Config = autoclass('net.sourceforge.zbar.Config')
       +SurfaceView = autoclass('android.view.SurfaceView')
       +LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
       +ImageFormat = autoclass('android.graphics.ImageFormat')
       +LinearLayout = autoclass('android.widget.LinearLayout')
       +
       +
       +class PreviewCallback(PythonJavaClass):
       +    '''Interface used to get back the preview frame of the Android Camera
       +    '''
       +    __javainterfaces__ = ('android.hardware.Camera$PreviewCallback', )
       +
       +    def __init__(self, callback):
       +        super(PreviewCallback, self).__init__()
       +        self.callback = callback
       +
       +    @java_method('([BLandroid/hardware/Camera;)V')
       +    def onPreviewFrame(self, data, camera):
       +        self.callback(camera, data)
       +
       +
       +class SurfaceHolderCallback(PythonJavaClass):
       +    '''Interface used to know exactly when the Surface used for the Android
       +    Camera will be created and changed.
       +    '''
       +
       +    __javainterfaces__ = ('android.view.SurfaceHolder$Callback', )
       +
       +    def __init__(self, callback):
       +        super(SurfaceHolderCallback, self).__init__()
       +        self.callback = callback
       + 
       +    @java_method('(Landroid/view/SurfaceHolder;III)V')
       +    def surfaceChanged(self, surface, fmt, width, height):
       +        self.callback(fmt, width, height)
       +
       +    @java_method('(Landroid/view/SurfaceHolder;)V')
       +    def surfaceCreated(self, surface):
       +        pass
       + 
       +    @java_method('(Landroid/view/SurfaceHolder;)V')
       +    def surfaceDestroyed(self, surface):
       +        pass
       +
       +
       +class AndroidWidgetHolder(Widget):
       +    '''Act as a placeholder for an Android widget.
       +    It will automatically add / remove the android view depending if the widget
       +    view is set or not. The android view will act as an overlay, so any graphics
       +    instruction in this area will be covered by the overlay.
       +    '''
       +
       +    view = ObjectProperty(allownone=True)
       +    '''Must be an Android View
       +    '''
       +
       +    def __init__(self, **kwargs):
       +        self._old_view = None
       +        from kivy.core.window import Window
       +        self._window = Window
       +        kwargs['size_hint'] = (None, None)
       +        super(AndroidWidgetHolder, self).__init__(**kwargs)
       +
       +    def on_view(self, instance, view):
       +        if self._old_view is not None:
       +            layout = cast(LinearLayout, self._old_view.getParent())
       +            layout.removeView(self._old_view)
       +            self._old_view = None
       +
       +        if view is None:
       +            return
       +
       +        activity = PythonActivity.mActivity
       +        activity.addContentView(view, LayoutParams(*self.size))
       +        view.setZOrderOnTop(True)
       +        view.setX(self.x)
       +        view.setY(self._window.height - self.y - self.height)
       +        self._old_view = view
       +
       +    def on_size(self, instance, size):
       +        if self.view:
       +            params = self.view.getLayoutParams()
       +            params.width = self.width
       +            params.height = self.height
       +            self.view.setLayoutParams(params)
       +            self.view.setY(self._window.height - self.y - self.height)
       +
       +    def on_x(self, instance, x):
       +        if self.view:
       +            self.view.setX(x)
       +
       +    def on_y(self, instance, y):
       +        if self.view:
       +            self.view.setY(self._window.height - self.y - self.height)
       +
       +
       +class AndroidCamera(Widget):
       +    '''Widget for controling an Android Camera.
       +    '''
       +
       +    index = NumericProperty(0)
       +
       +    __events__ = ('on_preview_frame', )
       +
       +    def __init__(self, **kwargs):
       +        self._holder = None
       +        self._android_camera = None
       +        super(AndroidCamera, self).__init__(**kwargs)
       +        self._holder = AndroidWidgetHolder(size=self.size, pos=self.pos)
       +        self.add_widget(self._holder)
       +
       +    @run_on_ui_thread
       +    def stop(self):
       +        if self._android_camera is None:
       +            return
       +        self._android_camera.setPreviewCallback(None)
       +        self._android_camera.release()
       +        self._android_camera = None
       +        self._holder.view = None
       +
       +    @run_on_ui_thread
       +    def start(self):
       +        if self._android_camera is not None:
       +            return
       +
       +        self._android_camera = Camera.open(self.index)
       +
       +        # create a fake surfaceview to get the previewCallback working.
       +        self._android_surface = SurfaceView(PythonActivity.mActivity)
       +        surface_holder = self._android_surface.getHolder()
       +
       +        # create our own surface holder to correctly call the next method when
       +        # the surface is ready
       +        self._android_surface_cb = SurfaceHolderCallback(self._on_surface_changed)
       +        surface_holder.addCallback(self._android_surface_cb)
       +
       +        # attach the android surfaceview to our android widget holder
       +        self._holder.view = self._android_surface
       +
       +    def _on_surface_changed(self, fmt, width, height):
       +        # internal, called when the android SurfaceView is ready
       +        # FIXME if the size is not handled by the camera, it will failed.
       +        params = self._android_camera.getParameters()
       +        params.setPreviewSize(width, height)
       +        self._android_camera.setParameters(params)
       +
       +        # now that we know the camera size, we'll create 2 buffers for faster
       +        # result (using Callback buffer approach, as described in Camera android
       +        # documentation)
       +        # it also reduce the GC collection
       +        bpp = ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8.
       +        buf = '\x00' * int(width * height * bpp)
       +        self._android_camera.addCallbackBuffer(buf)
       +        self._android_camera.addCallbackBuffer(buf)
       +
       +        # create a PreviewCallback to get back the onPreviewFrame into python
       +        self._previewCallback = PreviewCallback(self._on_preview_frame)
       +
       +        # connect everything and start the preview
       +        self._android_camera.setPreviewCallbackWithBuffer(self._previewCallback);
       +        self._android_camera.setPreviewDisplay(self._android_surface.getHolder())
       +        self._android_camera.startPreview();
       +
       +    def _on_preview_frame(self, camera, data):
       +        # internal, called by the PreviewCallback when onPreviewFrame is
       +        # received
       +        self.dispatch('on_preview_frame', camera, data)
       +        # reintroduce the data buffer into the queue
       +        self._android_camera.addCallbackBuffer(data)
       +
       +    def on_preview_frame(self, camera, data):
       +        pass
       +
       +    def on_size(self, instance, size):
       +        if self._holder:
       +            self._holder.size = size
       +
       +    def on_pos(self, instance, pos):
       +        if self._holder:
       +            self._holder.pos = pos
       +
       +
       +class ScannerAndroid(ScannerBase):
       +    '''Widget that use the AndroidCamera and zbar to detect qrcode.
       +    When found, the `symbols` will be updated
       +    '''
       +
       +    def __init__(self, **kwargs):
       +        super(ScannerAndroid, self).__init__(**kwargs)
       +        self._camera = AndroidCamera(
       +                size=self.camera_size,
       +                size_hint=(None, None))
       +        self._camera.bind(on_preview_frame=self._detect_qrcode_frame)
       +        self.add_widget(self._camera)
       +
       +        # create a scanner used for detecting qrcode
       +        self._scanner = ImageScanner()
       +        self._scanner.setConfig(0, Config.ENABLE, 0)
       +        self._scanner.setConfig(Symbol.QRCODE, Config.ENABLE, 1)
       +        self._scanner.setConfig(0, Config.X_DENSITY, 3)
       +        self._scanner.setConfig(0, Config.Y_DENSITY, 3)
       +
       +    def start(self):
       +        self._camera.start()
       +
       +    def stop(self):
       +        self._camera.stop()
       +
       +    def _detect_qrcode_frame(self, instance, camera, data):
       +        # the image we got by default from a camera is using the NV21 format
       +        # zbar only allow Y800/GREY image, so we first need to convert,
       +        # then start the detection on the image
       +        if not self.get_root_window():
       +            self.stop()
       +            return
       +        parameters = camera.getParameters()
       +        size = parameters.getPreviewSize()
       +        barcode = Image(size.width, size.height, 'NV21')
       +        barcode.setData(data)
       +        barcode = barcode.convert('Y800')
       +
       +        result = self._scanner.scanImage(barcode)
       +
       +        if result == 0:
       +            self.symbols = []
       +            return
       +
       +        # we detected qrcode! extract and dispatch them
       +        symbols = []
       +        it = barcode.getSymbols().iterator()
       +        while it.hasNext():
       +            symbol = it.next()
       +            qrcode = ScannerAndroid.Qrcode(
       +                type=symbol.getType(),
       +                data=symbol.getData(),
       +                quality=symbol.getQuality(),
       +                count=symbol.getCount(),
       +                bounds=symbol.getBounds())
       +            symbols.append(qrcode)
       +
       +        self.symbols = symbols
       +
       +    '''
       +    # can't work, due to the overlay.
       +    def on_symbols(self, instance, value):
       +        if self.show_bounds:
       +            self.update_bounds()
       +
       +    def update_bounds(self):
       +        self.canvas.after.remove_group('bounds')
       +        if not self.symbols:
       +            return
       +        with self.canvas.after:
       +            Color(1, 0, 0, group='bounds')
       +            for symbol in self.symbols:
       +                x, y, w, h = symbol.bounds
       +                x = self._camera.right - x - w
       +                y = self._camera.top - y - h
       +                Line(rectangle=[x, y, w, h], group='bounds')
       +    '''
       +
       +
       +if __name__ == '__main__':
       +    from kivy.lang import Builder
       +    from kivy.app import App
       +
       +    qrcode_kv = '''
       +BoxLayout:
       +    orientation: 'vertical'
       +
       +    ZbarQrcodeDetector:
       +        id: detector
       +
       +    Label:
       +        text: '\\n'.join(map(repr, detector.symbols))
       +        size_hint_y: None
       +        height: '100dp'
       +
       +    BoxLayout:
       +        size_hint_y: None
       +        height: '48dp'
       +
       +        Button:
       +            text: 'Scan a qrcode'
       +            on_release: detector.start()
       +        Button:
       +            text: 'Stop detection'
       +            on_release: detector.stop()
       +'''
       +
       +    class QrcodeExample(App):
       +        def build(self):
       +            return Builder.load_string(qrcode_kv)
       +
       +    QrcodeExample().run()
   DIR diff --git a/gui/kivy/qr_scanner/scanner_camera.py b/gui/kivy/qr_scanner/scanner_camera.py
       t@@ -0,0 +1,89 @@
       +from kivy.uix.camera import Camera
       +from kivy.clock import Clock
       +
       +import iconv
       +from electrum_gui.kivy.qr_scanner import ScannerBase
       +try:
       +    from zbar import ImageScanner, Config, Image, Symbol
       +except ImportError:
       +    raise SystemError('unable to import zbar please make sure you have it installed')
       +try:
       +    import Image as PILImage
       +except ImportError:
       +    raise SystemError('unable to import Pil/pillow please install one of the two.')
       +
       +__all__ = ('ScannerCamera', )
       +
       +class ScannerCamera(ScannerBase):
       +    '''Widget that use the kivy.uix.camera.Camera and zbar to detect qrcode.
       +    When found, the `symbols` will be updated
       +    '''
       +
       +    def __init__(self, **kwargs):
       +        super(ScannerCamera, self).__init__(**kwargs)
       +        self._camera = None
       +        # create a scanner used for detecting qrcode
       +        self._scanner = ImageScanner()
       +        self._scanner.parse_config('enable')
       +        #self._scanner.setConfig(Symbol.QRCODE, Config.ENABLE, 1)
       +        #self._scanner.setConfig(0, Config.X_DENSITY, 3)
       +        #self._scanner.setConfig(0, Config.Y_DENSITY, 3)
       +
       +    def start(self):
       +        if not self._camera:
       +            self._camera = Camera(
       +                    resolution=self.camera_size,
       +                    size_hint=(None, None))
       +            self.add_widget(self._camera)
       +            self.bind(size=self._camera.setter('size'))
       +            self.bind(pos=self._camera.setter('pos'))
       +        else:
       +            self._camera._camera.init_camera()
       +        self._camera.play = True
       +        Clock.schedule_interval(self._detect_qrcode_frame, 1/15)
       +
       +    def stop(self):
       +        if not self._camera:
       +            return
       +        self._camera.play = False
       +        Clock.unschedule(self._detect_qrcode_frame)
       +        # TODO: testing for various platforms(windows, mac)
       +        self._camera._camera._pipeline.set_state(1)
       +        #self._camera = None
       +
       +    def _detect_qrcode_frame(self, *args):
       +        # the image we got by default from a camera is using the rgba format
       +        # zbar only allow Y800/GREY image, so we first need to convert,
       +        # then start the detection on the image
       +        if not self.get_root_window():
       +            self.stop()
       +            return
       +        cam = self._camera
       +        tex = cam.texture
       +        if not tex:
       +            return
       +        im = PILImage.fromstring('RGBA', tex.size, tex.pixels)
       +        im = im.convert('L')
       +        barcode = Image(tex.size[0],
       +                        tex.size[1], 'Y800', im.tostring())
       +
       +        result = self._scanner.scan(barcode)
       +
       +        if result == 0:
       +            self.symbols = []
       +            del(barcode)
       +            return
       +
       +        # we detected qrcode! extract and dispatch them
       +        symbols = []
       +        for symbol in barcode.symbols:
       +            qrcode = ScannerCamera.Qrcode(
       +                type=symbol.type,
       +                data=symbol.data,
       +                quality=symbol.quality,
       +                count=symbol.count,
       +                bounds=symbol.location)
       +            symbols.append(qrcode)
       +
       +        self.symbols = symbols
       +        del(barcode)
   DIR diff --git a/gui/kivy/qrcodewidget.py b/gui/kivy/qrcodewidget.py
       t@@ -0,0 +1,179 @@
       +''' Kivy Widget that accepts data and displas qrcode
       +'''
       +
       +from threading import Thread
       +from functools import partial
       +
       +from kivy.uix.floatlayout import FloatLayout
       +
       +from kivy.graphics.texture import Texture
       +from kivy.properties import StringProperty
       +from kivy.properties import ObjectProperty, StringProperty, ListProperty,\
       +    BooleanProperty
       +from kivy.lang import Builder
       +from kivy.clock import Clock
       +
       +try:
       +    import qrcode
       +except ImportError:
       +    sys.exit("Error: qrcode does not seem to be installed. Try 'sudo pip install qrcode'")
       +
       +
       +
       +Builder.load_string('''
       +<QRCodeWidget>
       +    on_parent: if args[1]: qrimage.source = self.loading_image
       +    canvas.before:
       +        # Draw white Rectangle
       +        Color:
       +            rgba: root.background_color
       +        Rectangle:
       +            size: self.size
       +            pos: self.pos
       +    canvas.after:
       +        Color:
       +            rgba: .5, .5, .5, 1 if root.show_border else 0
       +        Line:
       +            width: dp(1.333)
       +            points:
       +                dp(2), dp(2),\
       +                self.width - dp(2), dp(2),\
       +                self.width - dp(2), self.height - dp(2),\
       +                dp(2), self.height - dp(2),\
       +                dp(2), dp(2)
       +    Image
       +        id: qrimage
       +        pos_hint: {'center_x': .5, 'center_y': .5}
       +        allow_stretch: True
       +        size_hint: None, None
       +        size: root.width * .9, root.height * .9
       +''')
       +
       +class QRCodeWidget(FloatLayout):
       +
       +    show_border = BooleanProperty(True)
       +    '''Whether to show border around the widget.
       +
       +    :data:`show_border` is a :class:`~kivy.properties.BooleanProperty`,
       +    defaulting to `True`.
       +    '''
       +
       +    data = StringProperty(None, allow_none=True)
       +    ''' Data using which the qrcode is generated.
       +
       +    :data:`data` is a :class:`~kivy.properties.StringProperty`, defaulting to
       +    `None`.
       +    '''
       +
       +    background_color = ListProperty((1, 1, 1, 1))
       +    ''' Background color of the background of the widget.
       +
       +    :data:`background_color` is a :class:`~kivy.properties.ListProperty`,
       +    defaulting to `(1, 1, 1, 1)`.
       +    '''
       +
       +    loading_image = StringProperty('gui/kivy/theming/loading.gif')
       +
       +    def __init__(self, **kwargs):
       +        super(QRCodeWidget, self).__init__(**kwargs)
       +        self.addr = None
       +        self.qr = None
       +        self._qrtexture = None
       +
       +    def on_data(self, instance, value):
       +        if not (self.canvas or value):
       +            return
       +        img = self.ids.get('qrimage', None)
       +
       +        if not img:
       +            # if texture hasn't yet been created delay the texture updation
       +            Clock.schedule_once(lambda dt: self.on_data(instance, value))
       +            return
       +        img.anim_delay = .25
       +        img.source = self.loading_image
       +        Thread(target=partial(self.generate_qr, value)).start()
       +
       +    def generate_qr(self, value):
       +        self.set_addr(value)
       +        self.update_qr()
       +
       +    def set_addr(self, addr):
       +        if self.addr == addr:
       +            return
       +        MinSize = 210 if len(addr) < 128 else 500
       +        self.setMinimumSize((MinSize, MinSize))
       +        self.addr = addr
       +        self.qr = None
       +
       +    def update_qr(self):
       +        if not self.addr and self.qr:
       +            return
       +        QRCode = qrcode.QRCode
       +        L = qrcode.constants.ERROR_CORRECT_L
       +        addr = self.addr
       +        try:
       +            self.qr = qr = QRCode(
       +                version=None,
       +                error_correction=L,
       +                box_size=10,
       +                border=0,
       +                )
       +            qr.add_data(addr)
       +            qr.make(fit=True)
       +        except Exception as e:
       +            print e
       +            self.qr=None
       +        self.update_texture()
       +
       +    def setMinimumSize(self, size):
       +        # currently unused, do we need this?
       +        self._texture_size = size
       +
       +    def _create_texture(self, k, dt):
       +        self._qrtexture = texture = Texture.create(size=(k,k), colorfmt='rgb')
       +        # don't interpolate texture
       +        texture.min_filter = 'nearest'
       +        texture.mag_filter = 'nearest'
       +
       +    def update_texture(self):
       +        if not self.addr:
       +            return
       +
       +        matrix = self.qr.get_matrix()
       +        k = len(matrix)
       +        # create the texture in main UI thread otherwise
       +        # this will lead to memory corruption
       +        Clock.schedule_once(partial(self._create_texture, k), -1)
       +        buff = []
       +        bext = buff.extend
       +        cr, cg, cb, ca = self.background_color[:]
       +        cr, cg, cb = cr*255, cg*255, cb*255
       +
       +        for r in range(k):
       +            for c in range(k):
       +                bext([0, 0, 0] if matrix[r][c] else [cr, cg, cb])
       +
       +        # then blit the buffer
       +        buff = ''.join(map(chr, buff))
       +        # update texture in UI thread.
       +        Clock.schedule_once(lambda dt: self._upd_texture(buff))
       +
       +    def _upd_texture(self, buff):
       +        texture = self._qrtexture
       +        if not texture:
       +            # if texture hasn't yet been created delay the texture updation
       +            Clock.schedule_once(lambda dt: self._upd_texture(buff))
       +            return
       +        texture.blit_buffer(buff, colorfmt='rgb', bufferfmt='ubyte')
       +        img =self.ids.qrimage
       +        img.anim_delay = -1
       +        img.texture = texture
       +        img.canvas.ask_update()
       +
       +if __name__ == '__main__':
       +    from kivy.app import runTouchApp
       +    import sys
       +    data = str(sys.argv[1:])
       +    runTouchApp(QRCodeWidget(data=data))
       +
       +
   DIR diff --git a/gui/kivy/screens.py b/gui/kivy/screens.py
       t@@ -0,0 +1,1095 @@
       +from functools import partial
       +import os, datetime, json, csv
       +
       +from kivy.app import App
       +from kivy.animation import Animation
       +from kivy.core.clipboard import Clipboard
       +from kivy.clock import Clock
       +from kivy.factory import Factory
       +from kivy.metrics import dp
       +from kivy.properties import (ObjectProperty, StringProperty, ListProperty,
       +                            DictProperty)
       +
       +from kivy.uix.button import Button
       +from kivy.uix.bubble import Bubble, BubbleButton
       +from kivy.uix.boxlayout import BoxLayout
       +from kivy.uix.label import Label
       +from kivy.uix.textinput import TextInput
       +from kivy.uix.screenmanager import Screen as Screen, ScreenManager
       +from kivy.uix.tabbedpanel import TabbedPanel
       +
       +
       +from electrum_gui.kivy.dialog import (NewContactDialog, TakeInputDialog,
       +        PrivateKeyDialog, SignVerifyDialog, MessageBox, MessageBoxError,
       +        SaveDialog, LoadDialog, InfoDialog, ImportPrivateKeysDialog, Dialog,
       +        EditLabelDialog, EditDescriptionDialog, ShowMasterPublicKeyDialog,
       +        RecentActivityDialog)
       +
       +from electrum_gui.i18n import _, languages
       +from electrum_gui.kivy.menus import ContextMenu
       +from electrum.interface import DEFAULT_PORTS
       +from electrum.verifier import WalletVerifier
       +from electrum.wallet import Wallet, WalletSynchronizer
       +from electrum.bitcoin import is_valid
       +
       +DEFAULT_PATH = '/tmp/'
       +
       +# Delayed imports
       +encode_uri = None
       +
       +
       +class CScreen(Screen):
       +
       +    __events__ = ('on_activate', 'on_deactivate')
       +
       +    action_view = ObjectProperty(None)
       +
       +    def _change_action_view(self):
       +        app = App.get_running_app()
       +        action_bar = app.root.main_screen.ids.action_bar
       +        _action_view = self.action_view
       +
       +        if (not _action_view) or _action_view.parent:
       +            return
       +        action_bar.clear_widgets()
       +        action_bar.add_widget(_action_view)
       +
       +    def on_activate(self):
       +        Clock.schedule_once(lambda dt: self._change_action_view())
       +
       +    def on_deactivate(self):
       +        Clock.schedule_once(lambda dt: self._change_action_view())
       +
       +
       +class RootManager(ScreenManager):
       +    '''Main Root Widget of the app'''
       +
       +    # initialize properties that will be updted in kv
       +    main_screen = ObjectProperty(None)
       +    '''Object holding the reference to main screen'''
       +
       +    screen_preferences = ObjectProperty(None)
       +    '''Object holding the reference to preferences screen'''
       +
       +    screen_seed = ObjectProperty(None)
       +    ''''''
       +
       +    screen_network = ObjectProperty(None)
       +    '''Object holding the Network screen'''
       +
       +
       +class MainScreen(Screen):
       +
       +    pass
       +
       +
       +class ScreenSend(CScreen):
       +
       +    pass
       +
       +
       +class ScreenDashboard(CScreen):
       +
       +    tab = ObjectProperty(None)
       +
       +    def show_tx_details(
       +        self, date, address, amount, amount_color, balance,
       +        tx_hash, conf, quote_text):
       +
       +        ra_dialog = RecentActivityDialog()
       +
       +        ra_dialog.address = address
       +        ra_dialog.amount = amount
       +        ra_dialog.amount_color = amount_color
       +        ra_dialog.confirmations = conf
       +        ra_dialog.quote_text = quote_text
       +        date_time = date.split()
       +        if len(date_time) == 2:
       +            ra_dialog.date = date_time[0]
       +            ra_dialog.time = date_time[1]
       +            ra_dialog.status = 'Validated'
       +        else:
       +            ra_dialog.date = date_time
       +            ra_dialog.status = 'Pending'
       +        ra_dialog.tx_hash = tx_hash
       +
       +        app = App.get_running_app()
       +        main_gui = app.gui.main_gui
       +        tx_hash = tx_hash
       +        tx = app.wallet.transactions.get(tx_hash)
       +
       +        if tx_hash in app.wallet.transactions.keys():
       +            is_relevant, is_mine, v, fee = app.wallet.get_tx_value(tx)
       +            conf, timestamp = app.wallet.verifier.get_confirmations(tx_hash)
       +            #if timestamp:
       +            #    time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
       +            #else:
       +            #    time_str = 'pending'
       +        else:
       +            is_mine = False
       +
       +        ra_dialog.is_mine = is_mine
       +
       +        if is_mine:
       +            if fee is not None: 
       +                ra_dialog.fee = main_gui.format_amount(fee)
       +            else:
       +                ra_dialog.fee = 'unknown'
       +
       +        ra_dialog.open()
       +
       +
       +class ScreenPassword(Screen):
       +
       +    __events__ = ('on_release', 'on_deactivate', 'on_activate')
       +
       +    def on_activate(self):
       +        app = App.get_running_app()
       +        action_bar = app.root.main_screen.ids.action_bar
       +        action_bar.add_widget(self._action_view)
       +
       +    def on_deactivate(self):
       +        self.ids.password.text = ''
       +
       +    def on_release(self, *args):
       +        pass
       +
       +
       +class SettingsScreen(Screen):
       +
       +    def __init__(self, **kwargs):
       +        super(SettingsScreen, self).__init__(**kwargs)
       +        Clock.schedule_once(self.delayed_init)
       +        self.app = App.get_running_app()
       +
       +    def on_enter(self, *args):
       +        self.delayed_init()
       +
       +    def delayed_init(self, *dt):
       +        app = self.app
       +        try:
       +            main_gui = app.gui.main_gui
       +        except AttributeError:
       +            # wait for main gui to start
       +            Clock.schedule_once(self.delayed_init, 1)
       +            return
       +        ids = self.ids
       +
       +        ids.st_unit_combo.key = main_gui.base_unit()
       +        ids.st_fee_e.text = main_gui.format_amount(app.wallet.fee).strip()
       +        ids.st_expert_cb.active = main_gui.expert_mode
       +
       +        currencies = main_gui.exchanger.get_currencies()
       +        currencies.insert(0, "None")
       +        currencies = zip(currencies, currencies)
       +        key = app.conf.get('currency', 'None')
       +        ids.st_cur_combo.text = ids.st_cur_combo.key = key
       +        ids.st_cur_combo.items = currencies
       +
       +        ids.st_lang_combo.key = key = app.conf.get("language", '')
       +        ids.st_lang_combo.items = languages.items()
       +        x, y = zip(*ids.st_lang_combo.items)
       +        ids.st_lang_combo.text = y[x.index(key)]
       +
       +    def do_callback(self, instance):
       +        ids = self.ids
       +        app = self.app
       +        wallet = app.wallet
       +        main_gui = app.gui.main_gui
       +
       +        if instance == ids.export_labels:
       +            title = _("Select file to save your labels")
       +            path = DEFAULT_PATH
       +            filename = "electrum_labels.dat"
       +            filters = ["*.dat"]
       +
       +            def save(instance):
       +                path = dialog.file_chooser.path
       +                filename = dialog.text_input.text.strip()
       +                labels = wallet.labels
       +                try:
       +                    with open(os.path.join(path, filename), 'w+') as stream:
       +                        json.dump(labels, stream)
       +                    MessageBox(title="Labels exported",
       +                               message=_("Your labels were exported to")\
       +                                   + " '%s'" % str(filename),
       +                               size=('320dp', '320dp')).open()
       +                except (IOError, os.error), reason:
       +                    MessageBoxError(
       +                        title="Unable to export labels",
       +                        message=_("Electrum was unable to export your labels.")+
       +                            "\n" + str(reason), size=('320dp', '320dp')).open()
       +                dialog.close()
       +
       +            dialog = SaveDialog(title=title,
       +                                path=path,
       +                                filename=filename,
       +                                filters=filters)
       +            dialog.save_button.bind(on_release=save)
       +            dialog.open()
       +
       +        elif instance == ids.import_labels:
       +            title = _("Open labels file")
       +            path = DEFAULT_PATH
       +            filename = ""
       +            filters = ["*.dat"]
       +
       +            def load(instance):
       +                path = dialog.file_chooser.path
       +                filename = dialog.text_input.text.strip()
       +
       +                labels = wallet.labels
       +                try:
       +                    with open(os.path.join(path, filename), 'r') as stream:
       +                        for key, value in json.loads(stream.read()).items():
       +                            wallet.labels[key] = value
       +                        wallet.save()
       +                    MessageBox(title="Labels imported",
       +                               message=_("Your labels were imported from") + " '%s'" % str(filename),
       +                               size=('320dp', '320dp')).open()
       +                except (IOError, os.error), reason:
       +                    MessageBoxError(title="Unable to import labels",
       +                                    message=_("Electrum was unable to import your labels.") + "\n" + str(reason),
       +                                    size=('320dp', '320dp')).open()
       +
       +                dialog.close()
       +
       +            dialog = LoadDialog(title=title, path=path, filename=filename, filters=filters)
       +            dialog.load_button.bind(on_press=load)
       +            dialog.open()
       +
       +        elif instance == ids.export_history:
       +            title = _("Select file to export your wallet transactions to")
       +            path = os.path.expanduser('~')
       +            filename = "electrum-history.csv"
       +            filters = ["*.csv"]
       +
       +            def save(instance):
       +                path = dialog.file_chooser.path
       +                filename = dialog.text_input.text.strip()
       +                # extracted from gui_lite.csv_transaction
       +                wallet = wallet
       +                try:
       +                    with open(os.path.join(path, filename), "w+") as stream:
       +                        transaction = csv.writer(stream)
       +                        transaction.writerow(["transaction_hash", "label", "confirmations", "value", "fee", "balance", "timestamp"])
       +                        for item in wallet.get_tx_history():
       +                            tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
       +                            if confirmations:
       +                                if timestamp is not None:
       +                                    try:
       +                                        time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
       +                                    except [RuntimeError, TypeError, NameError] as reason:
       +                                        time_string = "unknown"
       +                                        pass
       +                                else:
       +                                    time_string = "unknown"
       +                            else:
       +                                time_string = "pending"
       +
       +                            if value is not None:
       +                                value_string = format_satoshis(value, True, wallet.num_zeros)
       +                            else:
       +                                value_string = '--'
       +
       +                            if fee is not None:
       +                                fee_string = format_satoshis(fee, True, wallet.num_zeros)
       +                            else:
       +                                fee_string = '0'
       +
       +                            if tx_hash:
       +                                label, is_default_label = wallet.get_label(tx_hash)
       +                            else:
       +                                label = ""
       +
       +                            balance_string = format_satoshis(balance, False, wallet.num_zeros)
       +                            transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
       +                        MessageBox(title="CSV Export created",
       +                                   message="Your CSV export has been successfully created.",
       +                                   size=('320dp', '320dp')).open()
       +                except (IOError, os.error), reason:
       +                    export_error_label = _("Electrum was unable to produce a transaction export.")
       +                    MessageBoxError(title="Unable to create csv",
       +                                    message=export_error_label + "\n" + str(reason),
       +                                    size=('320dp', '320dp')).open()
       +                dialog.close()
       +
       +            dialog = SaveDialog(title=title, path=path, filename=filename, filters=filters)
       +            dialog.save_button.bind(on_press=save)
       +            dialog.open()
       +
       +        elif instance == ids.export_privkey:
       +            # NOTE: equivalent to @protected
       +            def protected_save_dialog(instance=None, password=None):
       +                def show_save_dialog(_dlg, instance):
       +                    _dlg.close()
       +                    title = _("Select file to export your private keys to")
       +                    path = DEFAULT_PATH
       +                    filename = "electrum-private-keys.csv"
       +                    filters = ["*.csv"]
       +
       +                    def save(instance):
       +                        path = dialog.file_chooser.path
       +                        filename = dialog.text_input.text.strip()
       +                        try:
       +                            with open(os.path.join(path, filename), "w+") as csvfile:
       +                                transaction = csv.writer(csvfile)
       +                                transaction.writerow(["address", "private_key"])
       +                                for addr, pk in wallet.get_private_keys(wallet.addresses(True), password).items():
       +                                    transaction.writerow(["%34s" % addr, pk])
       +                                MesageBox(message=_("Private keys exported."),
       +                                          size=('320dp', '320dp')).open()
       +                        except (IOError, os.error), reason:
       +                            export_error_label = _("Electrum was unable to produce a private key-export.")
       +                            return MessageBoxError(message="Unable to create csv", content_text=export_error_label + "\n" + str(reason),
       +                                            size=('320dp', '320dp')).open()
       +                        except BaseException, e:
       +                            return app.show_info_bubble(text=str(e))
       +
       +                        dialog.close()
       +
       +                    dialog = SaveDialog(title=title, path=path, filename=filename, filters=filters)
       +                    dialog.save_button.bind(on_press=save)
       +                    dialog.open()
       +
       +                mb = MessageBox(message="%s\n%s\n%s" % (
       +                                _("[color=ff0000ff][b]WARNING[/b][/color]: ALL your private keys are secret."),
       +                                _("Exposing a single private key can compromise your entire wallet!") + '\n\n',
       +                                _("In particular, [color=ff0000ff]DO NOT[/color] use 'redeem private key' services proposed by third parties.")),
       +                                on_release=show_save_dialog,
       +                                size = ('350dp', '320dp')).open()
       +
       +            if wallet.use_encryption:
       +                return main_gui.password_required_dialog(post_ok=protected_save_dialog)
       +            return protected_save_dialog()
       +
       +        elif instance == ids.import_privkey:
       +            # NOTE: equivalent to @protected
       +            def protected_load_dialog(_instance=None, password=None):
       +                def show_privkey_dialog(__instance=None):
       +
       +                    def on_release(_dlg, _btn):
       +                        if _btn.text == _('Cancel'):
       +                            _dlg.close()
       +                            confirm_dialog.close()
       +                            return
       +
       +                        text = _dlg.ids.ti.text.split()
       +                        badkeys = []
       +                        addrlist = []
       +                        for key in text:
       +                            try:
       +                                addr = wallet.import_key(key, password)
       +                            except BaseException as e:
       +                                badkeys.append(key)
       +                                continue
       +                            if not addr:
       +                                badkeys.append(key)
       +                            else:
       +                                addrlist.append(addr)
       +                        if addrlist:
       +                            MessageBox(title=_('Information'),
       +                                       message=_("The following addresses were added") + ':\n' + '\n'.join(addrlist),
       +                                       size=('320dp', '320dp')).open()
       +                        if badkeys:
       +                            MessageBoxError(title=_('Error'),
       +                                            message=_("The following inputs could not be imported") + ':\n' + '\n'.join(badkeys),
       +                                            size=('320dp', '320dp')).open()
       +                        main_gui.update_receive_tab()
       +                        main_gui.update_history_tab()
       +
       +                        if _instance is not None:  # called via callback
       +                            _dlg.close()
       +
       +                    ImportPrivateKeysDialog(on_release=on_release).open()
       +
       +                if not wallet.imported_keys:
       +
       +                    def on_release(_dlg, _btn):
       +                        _dlg.close
       +                        if _btn.text == _('No'):
       +                            return
       +                        show_privkey_dialog()
       +
       +                    confirm_dialog = MessageBoxError(title=_('Warning'),
       +                        message=_('Imported keys are not recoverable from seed.') + ' ' \
       +                        + _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '\n\n' \
       +                        + _('Are you sure you understand what you are doing?'),
       +                        size=('320dp', '320dp'),
       +                        on_release=on_release)
       +                    confirm_dialog.buttons = [_('No'), _('Yes')]
       +                    confirm_dialog.open()
       +                else:
       +                    show_privkey_dialog()
       +
       +            if wallet.use_encryption:
       +                return main_gui.password_required_dialog(
       +                    post_ok=protected_load_dialog)
       +            return protected_load_dialog()
       +
       +        elif instance == ids.show_pubkey:
       +            # NOTE: Kivy TextInput doesn't wrap long text. So must handle it manually
       +            pub_key = wallet.get_master_public_key()
       +            pub_key = '%s\n%s\n%s\n%s' % (pub_key[0:31], pub_key[32:63], pub_key[64:95], pub_key[96:127])
       +            ShowMasterPublicKeyDialog(text=pub_key).open()
       +
       +        elif instance == ids.from_file:
       +            title = _("Select your transaction file")
       +            path = DEFAULT_PATH
       +            filename = ""
       +            filters = ["*.txn"]
       +
       +            def load(instance):
       +                path = dialog.file_chooser.path
       +                filename = dialog.text_input.text.strip()
       +
       +                if not filename:
       +                    return
       +                try:
       +                    with open(os.path.join(path, filename), "r") as f:
       +                        file_content = f.read()
       +                except (ValueError, IOError, os.error), reason:
       +                    MessageBoxError(title="Unable to read file or no transaction found",
       +                                    message=_("Electrum was unable to open your transaction file") + "\n" + str(reason),
       +                                    size=('320dp', '320dp')).open()
       +
       +                tx_dict = main_gui.tx_dict_from_text(file_content)
       +                if tx_dict:
       +                    main_gui.create_process_transaction_window(tx_dict)
       +
       +                dialog.close()
       +
       +            dialog = LoadDialog(title=title, path=path, filename=filename, filters=filters)
       +            dialog.load_button.bind(on_press=load)
       +            dialog.open()
       +
       +        elif instance == ids.from_text:
       +            def load_transaction(_dlg, _btn):
       +                if _btn.text == _('Cancel'):
       +                    _dlg.close
       +                    return
       +                text = _dlg.ids.ti.text
       +                if not text:
       +                    return
       +                tx_dict = main_gui.tx_dict_from_text(text)
       +                if tx_dict:
       +                    main_gui.create_process_transaction_window(tx_dict)
       +                _dlg.close()
       +
       +            dialog = TakeInputDialog(on_release=load_transaction)
       +            dialog.title = title=_("Input raw transaction")
       +            dialog.open()
       +
       +        # End of do_callback() #
       +
       +    def on_ok(self, instance):
       +        ##########
       +        app = self.app
       +        main_gui = app.gui.main_gui
       +
       +        fee = unicode(self.ids.st_fee_e.text)
       +        try:
       +            fee = main_gui.read_amount(fee)
       +        except:
       +            return MessageBoxError(message=_('Invalid value') + ': %s' % fee).open()
       +
       +        app.wallet.set_fee(fee)
       +
       +        ##########
       +        nz = unicode(self.ids.st_nz_e.text)
       +        try:
       +            nz = int(nz)
       +            if nz > 8: nz = 8
       +        except:
       +            return MessageBoxError(message=_('Invalid value') + ':%s' % nz).open()
       +
       +        if app.wallet.num_zeros != nz:
       +            app.wallet.num_zeros = nz
       +            app.conf.set_key('num_zeros', nz, True)
       +            main_gui.update_history_tab()
       +            main_gui.update_receive_tab()
       +
       +        usechange_result = self.ids.st_usechange_cb.active
       +        if app.wallet.use_change != usechange_result:
       +            app.wallet.use_change = usechange_result
       +            app.conf.set_key('use_change', app.wallet.use_change, True)
       +
       +        unit_result = self.ids.st_unit_combo.text
       +        if main_gui.base_unit() != unit_result:
       +            main_gui.decimal_point = 8 if unit_result == 'BTC' else 5
       +            app.conf.set_key('decimal_point', main_gui.decimal_point, True)
       +            main_gui.update_history_tab()
       +            main_gui.update_status()
       +
       +        try:
       +            n = int(self.ids.st_gap_e.text)
       +        except:
       +            return MessageBoxError(message=_('Invalid value')).open()
       +
       +        if app.wallet.gap_limit != n:
       +            if app.wallet.change_gap_limit(n):
       +                main_gui.update_receive_tab()
       +                app.conf.set_key('gap_limit', app.wallet.gap_limit, True)
       +            else:
       +                MessageBoxError(Message=_('Invalid value')).open()
       +                # TODO: no return???
       +
       +        need_restart = False
       +
       +        lang_request = str(self.ids.st_lang_combo.key)
       +        if lang_request != app.conf.get('language'):
       +            app.conf.set_key("language", lang_request, True)  # TODO: why can't save unicode
       +            need_restart = True
       +
       +        cur_request = str(self.ids.st_cur_combo.text)
       +        if cur_request != app.conf.get('currency', "None"):
       +            app.conf.set_key('currency', cur_request, True)  # TODO: why can't save unicode
       +            main_gui.update_wallet()
       +
       +        main_gui.run_hook('close_settings_dialog')
       +
       +        if need_restart:
       +            MessageBox(message=_('Please restart Electrum to activate the new GUI settings')).open()
       +
       +        # from receive_tab_set_mode()
       +        main_gui.save_column_widths()
       +        main_gui.expert_mode = self.ids.st_expert_cb.active
       +        app.conf.set_key('classic_expert_mode', main_gui.expert_mode, True)
       +        main_gui.update_receive_tab()
       +
       +        # close
       +        app.root.current = 'main_screen'
       +
       +
       +class NetworkScreen(Screen):
       +
       +    status = StringProperty(_('Uninitialized'))
       +    '''status message displayed on top of screen'''
       +
       +    server = StringProperty('')
       +
       +    #servers = ListProperty([])
       +
       +    servers_view = ObjectProperty(None)
       +
       +    server_host = ObjectProperty(None)
       +
       +    server_port = ObjectProperty(None)
       +
       +    server_protocol = ObjectProperty(None)
       +
       +    proxy_host = ObjectProperty(None)
       +
       +    proxy_port = ObjectProperty(None)
       +
       +    proxy_mode = ObjectProperty(None)
       +
       +    protocol_names = ListProperty(['TCP', 'HTTP', 'SSL', 'HTTPS'])
       +
       +    protocol_letters = StringProperty('thsg')
       +
       +    proxy_names = ListProperty(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
       +
       +    proxy_keys = ListProperty(['none', 'socks4', 'socks5', 'http'])
       +
       +    autocycle_cb = ObjectProperty(None)
       +
       +    interface = ObjectProperty(None)
       +
       +    def __init__(self, **kwargs):
       +        self.initialized = True
       +        super(NetworkScreen, self).__init__(**kwargs)
       +        Clock.schedule_once(self._delayed_init)
       +
       +    def _delayed_init(self, dt):
       +        self.protocol = None
       +        self.app = app = App.get_running_app()
       +        self.conf = conf = app.conf
       +        self.wallet = wallet = app.wallet
       +        self.interface = interface = wallet.interface
       +
       +        if not self.initialized:
       +            if interface.is_connected:
       +                self.status = _("Connected to") + " %s" % (interface.host) + "\n%d " % (wallet.verifier.height) + _("blocks")
       +            else:
       +                self.status = _("Not connected")
       +        else:
       +            self.status = _("Please choose a server.") + "\n" + _("Select 'Cancel' if you are offline.")
       +        self.server = server = interface.server
       +
       +        self.servers = interface.get_servers()
       +
       +        self.servers_view.content_adapter.bind(on_selection_change=self.server_changed)
       +
       +        ########################
       +        if server:
       +            host, port, protocol = server.split(':')
       +            self.set_protocol(protocol)
       +            self.change_server(host, protocol)
       +        else:
       +            self.set_protocol('s')
       +
       +        ########################
       +        # TODO: review it
       +        # if not config.is_modifiable('server'):
       +        #    for w in [self.server_host, self.server_port, self.server_protocol, self.servers_list_widget]: w.setEnabled(False)
       +
       +        self.check_for_disable(None, 'none')
       +
       +        # if not wallet.config.is_modifiable('proxy'):
       +        #    for w in [proxy_host, proxy_port, proxy_mode]: w.setEnabled(False)
       +
       +        proxy_config = interface.proxy\
       +                if interface.proxy else\
       +                { "mode":"none", "host":"localhost", "port":"8080"}
       +        self.proxy_mode.key = proxy_config.get("mode")
       +        self.proxy_host.text = proxy_config.get("host")
       +        self.proxy_port.text = proxy_config.get("port")
       +
       +        # server = unicode( server_host.text ) + ':' + unicode( server_port.text ) + ':' + (protocol_letters[server_protocol.currentIndex()])
       +        # if proxy_mode.currentText() != 'NONE':
       +        #    proxy = { u'mode':unicode(proxy_mode.currenttext).lower(), u'host':unicode(proxy_host.text), u'port':unicode(proxy_port.text) }
       +        # else:
       +        #    proxy = None
       +
       +        self.autocycle_cb.active = conf.get('auto_cycle', True)
       +        if not conf.is_modifiable('auto_cycle'):
       +            self.autocycle_cb.active = False
       +
       +    def check_for_disable(self, instance, proxy_mode_key):
       +        if proxy_mode_key != 'none':
       +            self.proxy_host.disabled = False
       +            self.proxy_port.disabled = False
       +        else:
       +            self.proxy_host.disabled = True
       +            self.proxy_port.disabled = True
       +
       +    def on_cancel(self, *args):
       +        self.manager.current = 'main_screen'
       +
       +        # TODO: RuntimeError: threads can only be started once
       +        # interface.start(wait=False)
       +        # interface.send([('server.peers.subscribe', [])])
       +
       +        # generate the first addresses, in case we are offline
       +        self.wallet.synchronize()
       +
       +        verifier = WalletVerifier(self.interface, self.conf)
       +        verifier.start()
       +        self.wallet.set_verifier(verifier)
       +        synchronizer = WalletSynchronizer(self.wallet, self.conf)
       +        synchronizer.start()
       +
       +        if not self.initialized:
       +            self.app.gui.main_gui.change_password_dialog()
       +
       +    def on_ok(self, *args):
       +        self.manager.current = 'main_screen'
       +
       +        ################
       +        server = ':'.join([str(self.server_host.text),
       +                            str(self.server_port.text),
       +                            str(self.server_protocol.key)])
       +
       +        if self.proxy_mode.key != 'none':
       +            proxy = { 'mode':str(self.proxy_mode.key),
       +                        'host':str(self.proxy_host.text),
       +                        'port':str(self.proxy_port.text) }
       +        else:
       +            proxy = None
       +
       +        app = self.app
       +        conf = self.conf
       +        wallet = self.wallet
       +        interface = self.interface
       +        conf.set_key("proxy", proxy, True)
       +        conf.set_key("server", server, True)
       +        interface.set_server(server, proxy)
       +        conf.set_key('auto_cycle', self.autocycle_cb.active, True)
       +
       +        # generate the first addresses, in case we are offline
       +        if app.gui.action == 'create':
       +            app.wallet.synchronize()
       +            app.gui.change_password_dialog()
       +
       +        verifier = WalletVerifier(interface, conf)
       +        verifier.start()
       +        wallet.set_verifier(verifier)
       +        synchronizer = WalletSynchronizer(wallet, conf)
       +        synchronizer.start()
       +
       +        if app.gui.action == 'restore':
       +            initialized = self.initialized
       +            try:
       +                def on_complete(keep_it=False):
       +                    wallet.fill_addressbook()
       +                    #if not keep_it:
       +                    #    app.stop()
       +                    #    return
       +                    if not initialized:
       +                        app.gui.change_password_dialog()
       +
       +                app.gui.restore_wallet(on_complete=on_complete)
       +            except:
       +                import traceback, sys
       +                traceback.print_exc(file=sys.stdout)
       +                app.stop()
       +        if not interface.isAlive():
       +            interface.start(wait=False)
       +        interface.send([('server.peers.subscribe', [])])
       +
       +
       +    def init_servers_list(self):
       +        data = []
       +        for _host, d in self.servers.items():
       +            if d.get(self.protocol):
       +                pruning_level = d.get('pruning', '')
       +                data.append((_host, pruning_level))
       +        self.servers_view.content_adapter.data = data
       +
       +    def set_protocol(self, protocol):
       +        if protocol != self.protocol:
       +            self.protocol = protocol
       +            self.init_servers_list()
       +
       +    def on_change_protocol(self, instance, protocol_key):
       +        p = protocol_key
       +        host = unicode(self.server_host.text)
       +        pp = self.servers.get(host)
       +        if not pp:
       +            return
       +        if p not in pp.keys():
       +            p = pp.keys()[0]
       +        port = pp[p]
       +        self.server_host.text = host
       +        self.server_port.text = port
       +        self.set_protocol(p)
       +
       +    def server_changed(self, instance):
       +        try:
       +            index = instance.selection[0].index
       +        except (AttributeError, IndexError):
       +            return
       +        item = instance.get_data_item(index)
       +        self.change_server(item[0], self.protocol)
       +
       +    def change_server(self, host, protocol):
       +        pp = self.servers.get(host, DEFAULT_PORTS)
       +        if protocol:
       +            port = pp.get(protocol)
       +            if not port: protocol = None
       +
       +        if not protocol:
       +            if 's' in pp.keys():
       +                protocol = 's'
       +                port = pp.get(protocol)
       +            else:
       +                protocol = pp.keys()[0]
       +                port = pp.get(protocol)
       +
       +        self.server_host.text = host
       +        self.server_port.text = port
       +        self.server_protocol.text = self.protocol_names[self.protocol_letters.index(protocol)]
       +
       +        if not self.servers: return
       +        # TODO: what's this?
       +        # for p in protocol_letters:
       +        #    i = protocol_letters.index(p)
       +        #    j = self.server_protocol.model().index(i,0)
       +        #    #if p not in pp.keys(): # and self.interface.is_connected:
       +        #    #    self.server_protocol.model().setData(j, QVariant(0), Qt.UserRole-1)
       +        #    #else:
       +        #    #    self.server_protocol.model().setData(j, QVariant(33), Qt.UserRole-1)
       +
       +class ScreenAddress(CScreen):
       +
       +    labels  = DictProperty({})
       +    '''
       +    '''
       +
       +    tab =  ObjectProperty(None)
       +    ''' The tab associated With this Carousel
       +    '''
       +
       +class ScreenConsole(CScreen):
       +
       +    pass
       +
       +
       +class ScreenReceive(CScreen):
       +
       +    pass
       +
       +#TODO: move to wallet management
       +class ScreenReceive2(CScreen):
       +
       +    receive_view = ObjectProperty(None)
       +
       +    def __init__(self, **kwargs):
       +        self.context_menu = None
       +        super(ScreenReceive, self).__init__(**kwargs)
       +        self.app = App.get_running_app()
       +
       +    def on_receive_view(self, instance, value):
       +        if not value:
       +            return
       +        value.on_context_menu = self.on_context_menu
       +
       +    def on_menu_item_selected(self, instance, _menu, _btn):
       +        '''Called when any one of the bubble menu items is selected
       +        '''
       +        app = self.app
       +        main_gui = app.gui.main_gui
       +
       +        def delete_imported_key():
       +            def on_release(_dlg, _dlg_btn):
       +                if _dlg_btn.text == _('Cancel'):
       +                    _dlg.close()
       +                    return
       +                app.wallet.delete_imported_key(address)
       +                main_gui.update_receive_tab()
       +                main_gui.update_history_tab()
       +
       +            MessageBox(title=_('Delete imported key'),
       +                            message=_("Do you want to remove")
       +                                +" %s "%addr +_("from your wallet?"),
       +                            buttons=[_('Cancel'), _('OK')],
       +                            on_release=on_release).open()
       +
       +        def edit_label_dialog():
       +            # Show dialog to edit the label
       +            def save_label(_dlg, _dlg_btn):
       +                if _dlg_btn.text != _('Ok'):
       +                    return
       +                txt = _dlg.ids.ti.text
       +                if txt:
       +                    instance.parent.children[2].text = txt
       +                _dlg.close()
       +
       +            text = instance.parent.children[2].text
       +            dialog = EditLabelDialog(text=text,
       +                                     on_release=save_label).open()
       +
       +        def show_private_key_dialog():
       +            # NOTE: equivalent to @protected
       +            def protected_show_private_key(_instance=None, password=None):
       +                try:
       +                    pk = app.wallet.get_private_key(address, password)
       +                except BaseException, e:
       +                    app.show_info_bubble(text=str(e))
       +                    return
       +
       +                PrivateKeyDialog(address=address,
       +                                 private_key=pk).open()
       +
       +            if app.wallet.use_encryption:
       +                return main_gui.password_required_dialog(
       +                                            post_ok=protected_show_private_key)
       +            protected_show_private_key()
       +
       +        def show_sign_verify_dialog():
       +            def on_release(_dlg, _dlg_btn):
       +                if _dlg_btn.text != _('Ok'):
       +                    return
       +                if _dlg.ids.tabs.current_tab.text == _('Sign'):
       +                    # NOTE: equivalent to @protected
       +                    def protected_do_sign_message(instance=None, password=None):
       +                        try:
       +                            sig = app.wallet.sign_message(
       +                                    _dlg.ids.sign_address.text,
       +                                    _dlg.ids.sign_message.text,
       +                                    password)
       +                            _dlg.ids.sign_signature.text = sig
       +                        except BaseException, e:
       +                            app.show_info_bubble(text=str(e.message))
       +
       +                    if app.wallet.use_encryption:
       +                        return main_gui.password_required_dialog(
       +                                post_ok=protected_do_sign_message)
       +                    return protected_do_sign_message()
       +
       +                else:  # _('Verify')
       +                    if app.wallet.verify_message(
       +                            _dlg.ids.verify_address.text,
       +                            _dlg.ids.verify_signature.text,
       +                            _dlg.ids.verify_message.text):
       +                        app.show_info_bubble(text=_("Signature verified"))
       +                    else:
       +                        app.show_info_bubble(
       +                            text=_("Error: wrong signature"))
       +            SignVerifyDialog(on_release=on_release, address=address).open()
       +
       +        def toggle_freeze():
       +            if address in app.wallet.frozen_addresses:
       +                app.wallet.unfreeze(address)
       +            else:
       +                app.wallet.freeze(address)
       +            main_gui.update_receive_tab()
       +
       +        def toggle_priority(_dlg, _dlg_btn):
       +            if address in app.wallet.prioritized_addresses:
       +                app.wallet.unprioritize(address)
       +            else:
       +                app.wallet.prioritize(address)
       +            main_gui.update_receive_tab()
       +
       +        _menu.hide()
       +        address = instance.parent.children[3].text
       +
       +        if _btn.text == _('Copy to clipboard'):
       +            # copy data to clipboard
       +            Clipboard.put(instance.parent.children[3].text, 'UTF8_STRING')
       +        elif _btn.text == _('Edit label'):
       +            edit_label_dialog()
       +        elif _btn.text == _('Private key'):
       +            show_private_key_dialog()
       +        elif _btn.text == _('Sign message'):
       +            # sign message
       +            show_sign_verify_dialog()
       +        elif _btn.text == _('Remove_from_wallet'):
       +            delete_imported_key()
       +        elif _btn.text in (_('Freeze'), _('Unfreeze')):
       +            toggle_freeze()
       +        elif _btn.text in (_('Prioritize'), _('Unprioritize')):
       +            toggle_priority(_menu, _btn)
       +
       +
       +    def on_context_menu(self, instance):
       +        '''Called when list item is clicked.
       +        Objective: show bubble menu
       +        '''
       +        app = self.app
       +        address = instance.parent.children[3].text
       +        if not address or not is_valid(address): return
       +
       +        context_menu = ContextMenu(size_hint=(None, None),
       +                                size=('160dp', '160dp'),
       +                                orientation='vertical',
       +                                arrow_pos='left_mid',
       +                                buttons=[_('Copy to clipboard'),
       +                                        _('Edit label'),
       +                                        _('Private key'),
       +                                        _('Sign message')],
       +                                on_release=partial(self.on_menu_item_selected,
       +                                                   instance))
       +        if address in app.wallet.imported_keys:
       +            context_menu.buttons = context_menu.buttons +\
       +                                [_('Remove from wallet')]
       +            # TODO: test more this feature
       +
       +        if app.gui.main_gui.expert_mode:
       +            # TODO: show frozen, prioritized rows in different color
       +            # as original code
       +
       +            t = _("Unfreeze")\
       +                 if address in app.wallet.frozen_addresses else\
       +                 _("Freeze")
       +            context_menu.buttons = context_menu.buttons + [t]
       +            t = _("Unprioritize")\
       +                if address in app.wallet.prioritized_addresses else\
       +                _("Prioritize")
       +            context_menu.buttons = context_menu.buttons + [t]
       +        context_menu.show(pos=(instance.right, instance.top))
       +
       +
       +class ScreenContacts(CScreen):
       +
       +    def add_new_contact(self):
       +        NewContactDialog().open()
       +
       +
       +
       +class TabbedCarousel(TabbedPanel):
       +
       +    carousel = ObjectProperty(None)
       +
       +    def animate_tab_to_center(self, value):
       +        scrlv = self._tab_strip.parent
       +        if not scrlv:
       +            return
       +        self_center_x = scrlv.center_x
       +        vcenter_x = value.center_x
       +        diff_x = (self_center_x - vcenter_x)
       +        try:
       +            scroll_x = scrlv.scroll_x - (diff_x / scrlv.width)
       +        except ZeroDivisionError:
       +            pass
       +        mation = Animation(scroll_x=max(0, min(scroll_x, 1)), d=.25)
       +        mation.cancel_all(scrlv)
       +        mation.start(scrlv)
       +
       +    def on_current_tab(self, instance, value):
       +        if value.text == 'default_tab':
       +            return
       +        self.animate_tab_to_center(value)
       +
       +    def on_index(self, instance, value):
       +        current_slide = instance.current_slide
       +        if not hasattr(current_slide, 'tab'):
       +            return
       +        tab = current_slide.tab
       +        ct = self.current_tab
       +        try:
       +            if ct.text != tab.text:
       +                carousel = self.carousel
       +                carousel.slides[ct.slide].dispatch('on_deactivate')
       +                self.switch_to(tab)
       +                carousel.slides[tab.slide].dispatch('on_activate')
       +        except AttributeError:
       +            current_slide.dispatch('on_activate')
       +
       +    def switch_to(self, header):
       +        # we have to replace the functionality of the original switch_to
       +        if not header:
       +            return
       +        if not hasattr(header, 'slide'):
       +            header.content = self.carousel
       +            super(TabbedCarousel, self).switch_to(header)
       +            tab = self.tab_list[-1]
       +            self._current_tab = tab
       +            tab.state = 'down'
       +            return
       +
       +        carousel = self.carousel
       +        self.current_tab.state = "normal"
       +        header.state = 'down'
       +        self._current_tab = header
       +        # set the carousel to load  the appropriate slide
       +        # saved in the screen attribute of the tab head
       +        slide = carousel.slides[header.slide]
       +        if carousel.current_slide != slide:
       +            carousel.current_slide.dispatch('on_deactivate')
       +            carousel.load_slide(slide)
       +            slide.dispatch('on_activate')
       +
       +    def add_widget(self, widget, index=0):
       +        if isinstance(widget, Screen):
       +            self.carousel.add_widget(widget)
       +            return
       +        super(TabbedCarousel, self).add_widget(widget, index=index)
       +
       +
       +class TabbedScreens(TabbedPanel):
       +
       +    manager = ObjectProperty(None)
       +    '''Linked to the screen manager in kv'''
       +
       +    def switch_to(self, header):
       +        # we don't use default tab so skip
       +        if not hasattr(header, 'screen'):
       +            header.content = self.manager
       +            super(TabbedScreens, self).switch_to(header)
       +            return
       +        if not header.screen:
       +            return
       +        panel = self
       +        panel.current_tab.state = "normal"
       +        header.state = 'down'
       +        panel._current_tab = header
       +        self.manager.current = header.screen
       +
       +    def add_widget(self, widget, index=0):
       +        if isinstance(widget, Screen):
       +            self.manager.add_widget(widget)
       +            return
       +        super(TabbedScreens, self).add_widget(widget, index=index)
   DIR diff --git a/gui/kivy/statusbar.py b/gui/kivy/statusbar.py
       t@@ -0,0 +1,7 @@
       +from kivy.uix.boxlayout import BoxLayout
       +from kivy.properties import StringProperty
       +
       +
       +class StatusBar(BoxLayout):
       +
       +    text = StringProperty('')
   DIR diff --git a/gui/kivy/textinput.py b/gui/kivy/textinput.py
       t@@ -0,0 +1,14 @@
       +from kivy.uix.textinput import TextInput
       +from kivy.properties import OptionProperty
       +
       +class ELTextInput(TextInput):
       +
       +    def insert_text(self, substring, from_undo=False):
       +        if not from_undo:
       +            if self.input_type == 'numbers':
       +                numeric_list = map(str, range(10))
       +                if '.' not in self.text:
       +                    numeric_list.append('.')
       +                if substring not in numeric_list:
       +                    return
       +        super(ELTextInput, self).insert_text(substring, from_undo=from_undo)
   DIR diff --git a/gui/kivy/theming/light-0.png b/gui/kivy/theming/light-0.png
       Binary files differ.
   DIR diff --git a/gui/kivy/theming/light-1.png b/gui/kivy/theming/light-1.png
       Binary files differ.
   DIR diff --git a/gui/kivy/theming/light.atlas b/gui/kivy/theming/light.atlas
       t@@ -0,0 +1 @@
parazyd.org:70 /git/electrum/commit/30126c544b81bcb8ac6a510da9c9d42acf82ee28.gph:4751: line too long