#!/usr/bin/env python
import urwid
import urwid.raw_display

# Copyright (C) 2009 Andrew Psaltis
# This code is a part of wicd, which is licensed under the GNU GPL Version 2.

class MaskingEditException(Exception):
    pass

# Password-style edit
class MaskingEdit(urwid.Edit):
    """
    mask_mode = one of:
        "always" : everything is a '*' all of the time
        "no_focus" : everything is a '*' only when not in focus
        "off" : everything is always unmasked
    mask_char = the single character that masks all other characters in the field
    """
    def __init__(self, caption = "", edit_text = "", multiline = False,
            align = 'left', wrap = 'space', allow_tab = False,
            edit_pos = None, layout=None, mask_mode="always",mask_char='*'):
        if mask_mode not in ['always','no_focus','off']:
            raise MaskingEditException('Mask mode %s not supported' % mask_mode)
        self.mask_mode = mask_mode
        if len(mask_char) > 1:
            raise MaskingEditException('Masks of more than one character are not supported!')
        self.mask_char = mask_char
        self.__super.__init__(caption,edit_text,multiline,align,wrap,allow_tab,edit_pos,layout)

    def get_mask_mode(self):
        return self.mask_mode
    def set_mask_mode(self,mode):
        self.mask_mode = mode

    def get_masked_text(self):
        return self.mask_char*len(self.get_edit_text())

    def render(self,(maxcol,), focus=False):
        """ 
        Render edit widget and return canvas.  Include cursor when in
        focus.
        """
        # If we aren't masking anything ATM, then act like an Edit.
        # No problems.
        if self.mask_mode == "off" or (self.mask_mode == 'no_focus' and focus == True):
            canv = self.__super.render((maxcol,),focus)
            # The cache messes this thing up, because I am totally changing what
            # is displayed.
            self._invalidate()
            return canv

        # Else, we have a slight mess to deal with...
        self._shift_view_to_cursor = not not focus # force bool

        text, attr = self.get_text()
        text = text[:len(self.caption)]+self.get_masked_text()
        trans = self.get_line_translation( maxcol, (text,attr) )
        canv = urwid.canvas.apply_text_layout(text, attr, trans, maxcol)

        if focus:
            canv = urwid.CompositeCanvas(canv)
            canv.cursor = self.get_cursor_coords((maxcol,))

        return canv
def run():
    dim = ui.get_cols_rows()

    keys = True
    while True:
        if keys:
            ui.draw_screen(dim, frame.render(dim, True))
        keys = ui.get_input()

        for k in keys:
            #Send key to underlying widget:
            k = frame.keypress(dim, k)
            if k in ('up','page up'):
                frame.set_focus('body')
                # Uncommenting this line "fixes" the edit in this case
                #self._w.body.get_focus()[0].get_focus()._invalidate()
            elif k in ('down','page down'):
                frame.set_focus('footer')
        if "window resize" in keys:
            dim = ui.get_cols_rows()
        if "esc" in keys or 'Q' in keys:
            return False

e = MaskingEdit("Edit: ",mask_mode='no_focus')
b = urwid.Button("Button")
f = urwid.Filler(e)
frame = urwid.Frame(body=f,footer=b)

ui = urwid.raw_display.Screen()

ui.run_wrapper(run)

