class Widget(object):
    #  rect            Rect       bounds in parent's coordinates
    #  parent          Widget     containing widget
    #  subwidgets      [Widget]   contained widgets
    #  focus_switch    Widget     subwidget to receive key events
    #  fg_color        color      or None to inherit from parent
    #  bg_color        color      to fill background, or None
    #  visible         boolean
    #  border_width    int        width of border to draw around widget, or None
    #  border_color    color      or None to use widget foreground color
    #  tab_stop        boolean    stop on this widget when tabbing
    #  anchor          string     of 'ltrb'

    font = FontProperty('font')
    fg_color = ThemeProperty('fg_color')
    bg_color = ThemeProperty('bg_color')
    bg_image = ThemeProperty('bg_image')
    scale_bg = ThemeProperty('scale_bg')
    border_width = ThemeProperty('border_width')
    border_color = ThemeProperty('border_color')
    sel_color = ThemeProperty('sel_color')
    margin = ThemeProperty('margin')
    menu_bar = overridable_property('menu_bar')
    is_gl_container = overridable_property('is_gl_container')

    tab_stop = False
    enter_response = None
    cancel_response = None
    anchor = 'ltwh'
    debug_resize = False
    _menubar = None
    _visible = True
    _is_gl_container = False

    tooltip = None
    tooltipText = None

    def __init__(self, rect=None, **kwds):
        if rect and not isinstance(rect, Rect):
            raise TypeError("Widget rect not a pygame.Rect")
        self._rect = Rect(rect or (0, 0, 100, 100))
        self.parent = None
        self.subwidgets = []
        self.focus_switch = None
        self.is_modal = False
        self.set(**kwds)

    def set(self, **kwds):
        for name, value in kwds.iteritems():
            if not hasattr(self, name):
                raise TypeError("Unexpected keyword argument '%s'" % name)
            setattr(self, name, value)

    def get_rect(self):
        return self._rect

    def set_rect(self, x):
        old_size = self._rect.size
        self._rect = Rect(x)
        self._resized(old_size)

#    def get_anchor(self):
#        if self.hstretch:
#            chars ='lr'
#        elif self.hmove:
#            chars = 'r'
#        else:
#            chars = 'l'
#        if self.vstretch:
#            chars += 'tb'
#        elif self.vmove:
#            chars += 'b'
#        else:
#            chars += 't'
#        return chars
#
#    def set_anchor(self, chars):
#        self.hmove = 'r' in chars and not 'l' in chars
#        self.vmove = 'b' in chars and not 't' in chars
#        self.hstretch = 'r' in chars and 'l' in chars
#        self.vstretch = 'b' in chars and 't' in chars
#
#    anchor = property(get_anchor, set_anchor)

    resizing_axes = {'h': 'lr', 'v': 'tb'}
    resizing_values = {'': [0], 'm': [1], 's': [0, 1]}

    def set_resizing(self, axis, value):
        chars = self.resizing_axes[axis]
        anchor = self.anchor
        for c in chars:
            anchor = anchor.replace(c, '')
        for i in self.resizing_values[value]:
            anchor += chars[i]
        self.anchor = anchor + value

    def _resized(self, (old_width, old_height)):
        new_width, new_height = self._rect.size
        dw = new_width - old_width
        dh = new_height - old_height
        if dw or dh:
            self.resized(dw, dh)

    def resized(self, dw, dh):
        if self.debug_resize:
            print "Widget.resized:", self, "by", (dw, dh), "to", self.size
        for widget in self.subwidgets:
            widget.parent_resized(dw, dh)

    def parent_resized(self, dw, dh):
        debug_resize = self.debug_resize or self.parent.debug_resize
        if debug_resize:
            print "Widget.parent_resized:", self, "by", (dw, dh)
        left, top, width, height = self._rect
        move = False
        resize = False
        anchor = self.anchor
        if dw:
            factors = [1, 1, 1]  # left, width, right
            if 'r' in anchor:
                factors[2] = 0
            if 'w' in anchor:
                factors[1] = 0
            if 'l' in anchor:
                factors[0] = 0
            if any(factors):
                resize = factors[1]
                move = factors[0] or factors[2]
                #print "lwr", factors
                left += factors[0] * dw / sum(factors)
                width += factors[1] * dw / sum(factors)
                #left = (left + width) + factors[2] * dw / sum(factors) - width

        if dh:
            factors = [1, 1, 1]  # bottom, height, top
            if 't' in anchor:
                factors[2] = 0
            if 'h' in anchor:
                factors[1] = 0
            if 'b' in anchor:
                factors[0] = 0
            if any(factors):
                resize = factors[1]
                move = factors[0] or factors[2]
                #print "bht", factors
                top += factors[2] * dh / sum(factors)
                height += factors[1] * dh / sum(factors)
                #top = (top + height) + factors[0] * dh / sum(factors) - height

        if resize:
            if debug_resize:
                print "Widget.parent_resized: changing rect to", (left, top, width, height)
            self.rect = (left, top, width, height)
        elif move:
            if debug_resize:
                print "Widget.parent_resized: moving to", (left, top)
            self._rect.topleft = (left, top)

    rect = property(get_rect, set_rect)

    left = rect_property('left')
    right = rect_property('right')
    top = rect_property('top')
    bottom = rect_property('bottom')
    width = rect_property('width')
    height = rect_property('height')
    size = rect_property('size')
    topleft = rect_property('topleft')
    topright = rect_property('topright')
    bottomleft = rect_property('bottomleft')
    bottomright = rect_property('bottomright')
    midleft = rect_property('midleft')
    midright = rect_property('midright')
    midtop = rect_property('midtop')
    midbottom = rect_property('midbottom')
    center = rect_property('center')
    centerx = rect_property('centerx')
    centery = rect_property('centery')

    def get_visible(self):
        return self._visible

    def set_visible(self, x):
        self._visible = x

    visible = overridable_property('visible')

    def add(self, arg):
        if arg:
            if isinstance(arg, Widget):
                arg.set_parent(self)
            else:
                for item in arg:
                    self.add(item)

    def add_centered(self, widget):
        w, h = self.size
        widget.center = w // 2, h // 2
        self.add(widget)

    def remove(self, widget):
        if widget in self.subwidgets:
            widget.set_parent(None)

    def set_parent(self, parent):
        if parent is not self.parent:
            if self.parent:
                self.parent._remove(self)
            self.parent = parent
            if parent:
                parent._add(self)

    def all_parents(self):
        widget = self
        parents = []
        while widget.parent:
            parents.append(widget.parent)
            widget = widget.parent
        return parents

    def _add(self, widget):
        self.subwidgets.append(widget)
        if hasattr(widget, "idleevent"):
            #print "Adding idle handler for ", widget
            self.get_root().add_idle_handler(widget)

    def _remove(self, widget):
        if hasattr(widget, "idleevent"):
            #print "Removing idle handler for ", widget
            self.get_root().remove_idle_handler(widget)
        self.subwidgets.remove(widget)

        if self.focus_switch is widget:
            self.focus_switch = None

    def draw_all(self, surface):
        if self.visible:
            surf_rect = surface.get_rect()
            bg_image = self.bg_image
            if bg_image:
                assert isinstance(bg_image, Surface)
                if self.scale_bg:
                    bg_width, bg_height = bg_image.get_size()
                    width, height = self.size
                    if width > bg_width or height > bg_height:
                        hscale = width / bg_width
                        vscale = height / bg_height
                        bg_image = rotozoom(bg_image, 0.0, max(hscale, vscale))
                r = bg_image.get_rect()
                r.center = surf_rect.center
                surface.blit(bg_image, r)
            else:
                bg = self.bg_color
                if bg:
                    surface.fill(bg)
            self.draw(surface)
            bw = self.border_width
            if bw:
                bc = self.border_color or self.fg_color
                frame_rect(surface, bc, surf_rect, bw)
            for widget in self.subwidgets:
                sub_rect = widget.rect
                if debug_rect:
                    print "Widget: Drawing subwidget %s of %s with rect %s" % (
                        widget, self, sub_rect)
                sub_rect = surf_rect.clip(sub_rect)
                if sub_rect.width > 0 and sub_rect.height > 0:
                    try:
                        sub = surface.subsurface(sub_rect)
                    except ValueError, e:
                        if str(e) == "subsurface rectangle outside surface area":
                            self.diagnose_subsurface_problem(surface, widget)
                        else:
                            raise
                    else:
                        widget.draw_all(sub)
            self.draw_over(surface)

    def diagnose_subsurface_problem(self, surface, widget):
        mess = "Widget %s %s outside parent surface %s %s" % (
            widget, widget.rect, self, surface.get_rect())
        sys.stderr.write("%s\n" % mess)
        surface.fill((255, 0, 0), widget.rect)

    def draw(self, surface):
        pass

    def draw_over(self, surface):
        pass

    def find_widget(self, pos):
        for widget in self.subwidgets[::-1]:
            if widget.visible:
                r = widget.rect
                if r.collidepoint(pos):
                    return widget.find_widget(subtract(pos, r.topleft))
        return self

    def handle_mouse(self, name, event):
        self.augment_mouse_event(event)
        self.call_handler(name, event)
        self.setup_cursor(event)

    def mouse_down(self, event):
        self.call_parent_handler("mouse_down", event)

    def mouse_up(self, event):
        self.call_parent_handler("mouse_up", event)

    def augment_mouse_event(self, event):
        event.dict['local'] = self.global_to_local(event.pos)

    def setup_cursor(self, event):
        global current_cursor
        cursor = self.get_cursor(event) or arrow_cursor
        if cursor is not current_cursor:
            set_cursor(*cursor)
            current_cursor = cursor

    def dispatch_key(self, name, event):
        if self.visible:

            if event.cmd and event.type == KEYDOWN:
                menubar = self._menubar
                if menubar and menubar.handle_command_key(event):
                    return
            widget = self.focus_switch
            if widget:
                widget.dispatch_key(name, event)
            else:
                self.call_handler(name, event)
        else:
            self.call_parent_handler(name, event)

    def get_focus(self):
        widget = self
        while 1:
            focus = widget.focus_switch
            if not focus:
                break
            widget = focus
        return widget

    def notify_attention_loss(self):
        widget = self
        while 1:
            if widget.is_modal:
                break
            parent = widget.parent
            if not parent:
                break
            focus = parent.focus_switch
            if focus and focus is not widget:
                focus.dispatch_attention_loss()
            widget = parent

    def dispatch_attention_loss(self):
        widget = self
        while widget:
            widget.attention_lost()
            widget = widget.focus_switch

    def attention_lost(self):
        pass

    def handle_command(self, name, *args):
        method = getattr(self, name, None)
        if method:
            return method(*args)
        else:
            parent = self.next_handler()
            if parent:
                return parent.handle_command(name, *args)

    def next_handler(self):
        if not self.is_modal:
            return self.parent

    def call_handler(self, name, *args):
        method = getattr(self, name, None)
        if method:
            return method(*args)
        else:
            return 'pass'

    def call_parent_handler(self, name, *args):
        parent = self.next_handler()
        if parent:
            parent.call_handler(name, *args)

    def global_to_local(self, p):
        return subtract(p, self.local_to_global_offset())

    def local_to_global(self, p):
        return add(p, self.local_to_global_offset())

    def local_to_global_offset(self):
        d = self.topleft
        parent = self.parent
        if parent:
            d = add(d, parent.local_to_global_offset())
        return d

    def key_down(self, event):
        k = event.key
        #print "Widget.key_down:", k ###
        if k == K_RETURN or k == K_KP_ENTER:
            if self.enter_response is not None:
                self.dismiss(self.enter_response)
                return
        elif k == K_ESCAPE:
            if self.cancel_response is not None:
                self.dismiss(self.cancel_response)
                return
        elif k == K_TAB:
            self.tab_to_next()
            return
        self.call_parent_handler('key_down', event)

    def key_up(self, event):
        self.call_parent_handler('key_up', event)

    def is_inside(self, container):
        widget = self
        while widget:
            if widget is container:
                return True
            widget = widget.parent
        return False

    @property
    def is_hover(self):
        return self.get_root().hover_widget is self

    def present(self, centered=True):
        #print "Widget: presenting with rect", self.rect
        root = self.get_root()
        if centered:
            self.center = root.center
        root.add(self)
        try:
            root.run_modal(self)
            self.dispatch_attention_loss()
        finally:
            root.remove(self)
        #print "Widget.present: returning", self.modal_result
        return self.modal_result

    def dismiss(self, value=True):
        self.modal_result = value

    def get_root(self):
        # Deprecated, use root.get_root()
        return root_widget

    def get_top_widget(self):
        top = self
        while top.parent and not top.is_modal:
            top = top.parent
        return top

    def focus(self):
        parent = self.next_handler()
        if parent:
            parent.focus_on(self)

    def focus_on(self, subwidget):
        old_focus = self.focus_switch
        if old_focus is not subwidget:
            if old_focus:
                old_focus.dispatch_attention_loss()
            self.focus_switch = subwidget
        self.focus()

    def has_focus(self):
        return self.is_modal or (self.parent and self.parent.focused_on(self))

    def focused_on(self, widget):
        return self.focus_switch is widget and self.has_focus()

    def focus_chain(self):
        result = []
        widget = self
        while widget:
            result.append(widget)
            widget = widget.focus_switch
        return result

    def shrink_wrap(self):
        contents = self.subwidgets
        if contents:
            rects = [widget.rect for widget in contents]
            #rmax = Rect.unionall(rects) # broken in PyGame 1.7.1
            rmax = rects.pop()
            for r in rects:
                rmax = rmax.union(r)
            self._rect.size = add(rmax.topleft, rmax.bottomright)

    def invalidate(self):
        root = self.get_root()
        if root:
            root.do_draw = True

    def get_cursor(self, event):
        return arrow_cursor

    def predict(self, kwds, name):
        try:
            return kwds[name]
        except KeyError:
            return theme.root.get(self.__class__, name)

    def predict_attr(self, kwds, name):
        try:
            return kwds[name]
        except KeyError:
            return getattr(self, name)

    def init_attr(self, kwds, name):
        try:
            return kwds.pop(name)
        except KeyError:
            return getattr(self, name)

    def predict_font(self, kwds, name='font'):
        return kwds.get(name) or theme.root.get_font(self.__class__, name)

    def get_margin_rect(self):
        r = Rect((0, 0), self.size)
        d = -2 * self.margin
        r.inflate_ip(d, d)
        return r

    def set_size_for_text(self, width, nlines=1):
        if width is not None:
            font = self.font
            d = 2 * self.margin
            if isinstance(width, basestring):
                width, height = font.size(width)
                width += d + 2
            else:
                height = font.size("X")[1]
            self.size = (width, height * nlines + d)

    def tab_to_first(self):
        chain = self.get_tab_order()
        if chain:
            chain[0].focus()

    def tab_to_next(self):
        top = self.get_top_widget()
        chain = top.get_tab_order()
        try:
            i = chain.index(self)
        except ValueError:
            return
        target = chain[(i + 1) % len(chain)]
        target.focus()

    def get_tab_order(self):
        result = []
        self.collect_tab_order(result)
        return result

    def collect_tab_order(self, result):
        if self.visible:
            if self.tab_stop:
                result.append(self)
            for child in self.subwidgets:
                child.collect_tab_order(result)

#    def tab_to_first(self, start = None):
#        if debug_tab:
#            print "Enter Widget.tab_to_first:", self ###
#            print "...start =", start ###
#        if not self.visible:
#            if debug_tab: print "...invisible" ###
#            self.tab_to_next_in_parent(start)
#        elif self.tab_stop:
#            if debug_tab: print "...stopping here" ###
#            self.focus()
#        else:
#            if debug_tab: print "...tabbing to next" ###
#            self.tab_to_next(start or self)
#        if debug_tab: print "Exit Widget.tab_to_first:", self ###
#
#    def tab_to_next(self, start = None):
#        if debug_tab:
#            print "Enter Widget.tab_to_next:", self ###
#            print "...start =", start ###
#        sub = self.subwidgets
#        if sub:
#            if debug_tab: print "...tabbing to first subwidget" ###
#            sub[0].tab_to_first(start or self)
#        else:
#            if debug_tab: print "...tabbing to next in parent" ###
#            self.tab_to_next_in_parent(start)
#        if debug_tab: print "Exit Widget.tab_to_next:", self ###
#
#    def tab_to_next_in_parent(self, start):
#        if debug_tab:
#            print "Enter Widget.tab_to_next_in_parent:", self ###
#            print "...start =", start ###
#        parent = self.parent
#        if parent and not self.is_modal:
#            if debug_tab: print "...telling parent to tab to next" ###
#            parent.tab_to_next_after(self, start)
#        else:
#            if self is not start:
#                if debug_tab: print "...wrapping back to first" ###
#                self.tab_to_first(start)
#        if debug_tab: print "Exit Widget.tab_to_next_in_parent:", self ###
#
#    def tab_to_next_after(self, last, start):
#        if debug_tab:
#            print "Enter Widget.tab_to_next_after:", self, last ###
#            print "...start =", start ###
#        sub = self.subwidgets
#        i = sub.index(last) + 1
#        if debug_tab: print "...next index =", i, "of", len(sub) ###
#        if i < len(sub):
#            if debug_tab: print "...tabbing there" ###
#            sub[i].tab_to_first(start)
#        else:
#            if debug_tab: print "...tabbing to next in parent" ###
#            self.tab_to_next_in_parent(start)
#        if debug_tab: print "Exit Widget.tab_to_next_after:", self, last ###

    def inherited(self, attribute):
        value = getattr(self, attribute)
        if value is not None:
            return value
        else:
            parent = self.next_handler()
            if parent:
                return parent.inherited(attribute)

    def __contains__(self, event):
        r = Rect(self._rect)
        r.left = 0
        r.top = 0
        p = self.global_to_local(event.pos)
        return r.collidepoint(p)

    def get_mouse(self):
        root = self.get_root()
        return root.get_mouse_for(self)

    def get_menu_bar(self):
        return self._menubar

    def set_menu_bar(self, menubar):
        if menubar is not self._menubar:
            if self._menubar:
                self.remove(self._menubar)
            self._menubar = menubar
            if menubar:
                if menubar.width == 0:
                    menubar.width = self.width
                    menubar.anchor = 'lr'
                self.add(menubar)

    def get_is_gl_container(self):
        return self._is_gl_container

    def set_is_gl_container(self, x):
        self._is_gl_container = x

    def gl_draw_all(self, root, offset):
        if not self.visible:
            return
        from OpenGL import GL, GLU
        rect = self.rect.move(offset)
        if self.is_gl_container:
            self.gl_draw_self(root, offset)
            suboffset = rect.topleft
            for subwidget in self.subwidgets:
                subwidget.gl_draw_all(root, suboffset)
        else:
            try:
                surface = Surface(self.size, SRCALPHA)
            except Exception, e:
                #size error?
                return
            self.draw_all(surface)
            data = image.tostring(surface, 'RGBA', 1)
            w, h = root.size
            GL.glViewport(0, 0, w, h)
            GL.glMatrixMode(GL.GL_PROJECTION)
            GL.glLoadIdentity()
            GLU.gluOrtho2D(0, w, 0, h)
            GL.glMatrixMode(GL.GL_MODELVIEW)
            GL.glLoadIdentity()
            GL.glRasterPos2i(max(rect.left, 0), max(h - rect.bottom, 0))
            GL.glPushAttrib(GL.GL_COLOR_BUFFER_BIT)
            GL.glEnable(GL.GL_BLEND)
            GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
            GL.glDrawPixels(self.width, self.height,
                GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, fromstring(data, dtype='uint8'))
            GL.glPopAttrib()
            GL.glFlush()

    def gl_draw_self(self, root, offset):
        pass class Dialog(Modal, Widget):

    click_outside_response = None

    def __init__(self, client=None, responses=None,
            default=0, cancel=-1, **kwds):
        Widget.__init__(self, **kwds)
        if client or responses:
            rows = []
            w1 = 0
            w2 = 0
            if client:
                rows.append(client)
                w1 = client.width
            if responses:
                buttons = Row([
                    Button(text, action=lambda t=text: self.dismiss(t))
                        for text in responses])
                rows.append(buttons)
                w2 = buttons.width
            if w1 < w2:
                a = 'l'
            else:
                a = 'r'
            contents = Column(rows, align=a)
            m = self.margin
            contents.topleft = (m, m)
            self.add(contents)
            self.shrink_wrap()
        if responses and default is not None:
            self.enter_response = responses[default]
        if responses and cancel is not None:
            self.cancel_response = responses[cancel]

    def mouse_down(self, e):
        if not e in self:
            response = self.click_outside_response
            if response is not None:
                self.dismiss(response) def ask(mess, responses=["OK", "Cancel"], default=0, cancel=-1,
        wrap_width=60, **kwds):
    box = Dialog(**kwds)
    d = box.margin
    lb = wrapped_label(mess, wrap_width)
    lb.topleft = (d, d)
    buts = []
    for caption in responses:
        but = Button(caption, action=lambda x=caption: box.dismiss(x))
        buts.append(but)
    brow = Row(buts, spacing=d)
    lb.width = max(lb.width, brow.width)
    col = Column([lb, brow], spacing=d, align='r')
    col.topleft = (d, d)
    if default is not None:
        box.enter_response = responses[default]
        buts[default].is_default = True
    else:
        box.enter_response = None
    if cancel is not None:
        box.cancel_response = responses[cancel]
    else:
        box.cancel_response = None
    box.add(col)
    box.shrink_wrap()
    return box.present() def alert(mess, **kwds):
    ask(mess, ["OK"], **kwds) class Label(Widget):

    text = overridable_property('text')
    align = overridable_property('align')

    hover_color = ThemeProperty('hover_color')
    highlight_color = ThemeProperty('highlight_color')
    disabled_color = ThemeProperty('disabled_color')
    highlight_bg_color = ThemeProperty('highlight_bg_color')
    hover_bg_color = ThemeProperty('hover_bg_color')
    enabled_bg_color = ThemeProperty('enabled_bg_color')
    disabled_bg_color = ThemeProperty('disabled_bg_color')

    enabled = True
    highlighted = False
    _align = 'l'

    def __init__(self, text, width=None, **kwds):
        Widget.__init__(self, **kwds)
        font = self.font
        lines = text.split("\n")
        tw, th = 0, 0
        for line in lines:
            w, h = font.size(line)
            tw = max(tw, w)
            th += h
        if width is not None:
            tw = width
        else:
            tw = max(1, tw)
        d = 2 * self.margin
        self.size = (tw + d, th + d)
        self._text = text

    def __repr__(self):
        return "Label {0}, child of {1}".format(self.text, self.parent)

    def get_text(self):
        return self._text

    def set_text(self, x):
        self._text = x

    def get_align(self):
        return self._align

    def set_align(self, x):
        self._align = x

    def draw(self, surface):
        if not self.enabled:
            fg = self.disabled_color
            bg = self.disabled_bg_color
        else:
            fg = self.fg_color
            bg = self.enabled_bg_color
            if self.is_default:
                fg = self.default_choice_color or fg
                bg = self.default_choice_bg_color or bg
            if self.is_hover:
                fg = self.hover_color or fg
                bg = self.hover_bg_color or bg
            if self.highlighted:
                fg = self.highlight_color or fg
                bg = self.highlight_bg_color or bg

        self.draw_with(surface, fg, bg)

    is_default = False

    def draw_with(self, surface, fg, bg=None):
        if bg:
            r = surface.get_rect()
            b = self.border_width
            if b:
                e = -2 * b
                r.inflate_ip(e, e)
            surface.fill(bg, r)
        m = self.margin
        align = self.align
        width = surface.get_width()
        y = m
        lines = self.text.split("\n")
        font = self.font
        dy = font.get_linesize()
        for line in lines:
            if len(line):
                size = font.size(line)
                if size[0] == 0:
                    continue

                image = font.render(line, True, fg)
                r = image.get_rect()
                r.top = y
                if align == 'l':
                    r.left = m
                elif align == 'r':
                    r.right = width - m
                else:
                    r.centerx = width // 2
                surface.blit(image, r)
            y += dy class Button(ButtonBase, Label):

    def __init__(self, text, action=None, enable=None, **kwds):
        if action:
            kwds['action'] = action
        if enable:
            kwds['enable'] = enable
        Label.__init__(self, text, **kwds) class TextField(Field):
    type = unicode
    _value = u"" class Row(RowOrColumn):

    d = (1, 0)
    axis = 'h'
    longways = 'width'
    crossways = 'height'
    align_map = {
        't': (0, 'topleft', 'topright'),
        'c': (1, 'midleft', 'midright'),
        'b': (2, 'bottomleft', 'bottomright'),
    }

    def __init__(self, items, width=None, **kwds):
        """
        Row(items, align=alignment, spacing=10, width=None, expand=None)
        align = 't', 'c' or 'b'
        """
        RowOrColumn.__init__(self, width, items, kwds) class Column(RowOrColumn):

    d = (0, 1)
    axis = 'v'
    longways = 'height'
    crossways = 'width'
    align_map = {
        'l': (0, 'topleft', 'bottomleft'),
        'c': (1, 'midtop', 'midbottom'),
        'r': (2, 'topright', 'bottomright'),
    }

    def __init__(self, items, height=None, **kwds):
        """
        Column(items, align=alignment, spacing=10, height=None, expand=None)
        align = 'l', 'c' or 'r'
        """
        RowOrColumn.__init__(self, height, items, kwds) class PaletteView(GridView):
    #  nrows   int   No. of displayed rows
    #  ncols   int   No. of displayed columns
    #
    #  Abstract methods:
    #
    #    num_items()  -->  no. of items
    #    draw_item(surface, item_no, rect)
    #    click_item(item_no, event)
    #    item_is_selected(item_no)  -->  bool

    sel_width = ThemeProperty('sel_width')
    zebra_color = ThemeProperty('zebra_color')
    scroll_button_size = ThemeProperty('scroll_button_size')
    scroll_button_color = ThemeProperty('scroll_button_color')
    highlight_style = ThemeProperty('highlight_style')
        # 'frame' or 'fill' or 'reverse' or None

    def __init__(self, cell_size, nrows, ncols, scrolling=False, **kwds):
        GridView.__init__(self, cell_size, nrows, ncols, **kwds)
        self.scrolling = scrolling
        if scrolling:
            d = self.scroll_button_size
            #l = self.width
            #b = self.height
            self.width += d
            #self.scroll_up_rect = Rect(l, 0, d, d).inflate(-4, -4)
            #self.scroll_down_rect = Rect(l, b - d, d, d).inflate(-4, -4)
        self.scroll = 0

    def scroll_up_rect(self):
        d = self.scroll_button_size
        r = Rect(0, 0, d, d)
        m = self.margin
        r.top = m
        r.right = self.width - m
        r.inflate_ip(-4, -4)
        return r

    def scroll_down_rect(self):
        d = self.scroll_button_size
        r = Rect(0, 0, d, d)
        m = self.margin
        r.bottom = self.height - m
        r.right = self.width - m
        r.inflate_ip(-4, -4)
        return r

    def draw(self, surface):

        GridView.draw(self, surface)
        if self.can_scroll_up():
            self.draw_scroll_up_button(surface)
        if self.can_scroll_down():
            self.draw_scroll_down_button(surface)

    def draw_scroll_up_button(self, surface):
        r = self.scroll_up_rect()
        c = self.scroll_button_color
        draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright])

    def draw_scroll_down_button(self, surface):
        r = self.scroll_down_rect()
        c = self.scroll_button_color
        draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright])

    def draw_cell(self, surface, row, col, rect):
        i = self.cell_to_item_no(row, col)
        if i is not None:
            highlight = self.item_is_selected(i)
            self.draw_item_and_highlight(surface, i, rect, highlight)

    def draw_item_and_highlight(self, surface, i, rect, highlight):
        if i % 2:
            surface.fill(self.zebra_color, rect)
        if highlight:
            self.draw_prehighlight(surface, i, rect)
        if highlight and self.highlight_style == 'reverse':
            fg = self.inherited('bg_color') or self.sel_color
        else:
            fg = self.fg_color
        self.draw_item_with(surface, i, rect, fg)
        if highlight:
            self.draw_posthighlight(surface, i, rect)

    def draw_item_with(self, surface, i, rect, fg):
        old_fg = self.fg_color
        self.fg_color = fg
        try:
            self.draw_item(surface, i, rect)
        finally:
            self.fg_color = old_fg

    def draw_prehighlight(self, surface, i, rect):
        if self.highlight_style == 'reverse':
            color = self.fg_color
        else:
            color = self.sel_color
        self.draw_prehighlight_with(surface, i, rect, color)

    def draw_prehighlight_with(self, surface, i, rect, color):
        style = self.highlight_style
        if style == 'frame':
            frame_rect(surface, color, rect, self.sel_width)
        elif style == 'fill' or style == 'reverse':
            surface.fill(color, rect)

    def draw_posthighlight(self, surface, i, rect):
        pass

    def mouse_down(self, event):
        if event.button == 1:
            if self.scrolling:
                p = event.local
                if self.scroll_up_rect().collidepoint(p):
                    self.scroll_up()
                    return
                elif self.scroll_down_rect().collidepoint(p):
                    self.scroll_down()
                    return
        if event.button == 4:
            self.scroll_up()
        if event.button == 5:
            self.scroll_down()

        GridView.mouse_down(self, event)

    def scroll_up(self):
        if self.can_scroll_up():
            self.scroll -= self.items_per_page() / 2

    def scroll_down(self):
        if self.can_scroll_down():
            self.scroll += self.items_per_page() / 2

    def scroll_to_item(self, n):
        i = max(0, min(n, self.num_items() - 1))
        p = self.items_per_page()
        self.scroll = p * (i // p)

    def can_scroll_up(self):
        return self.scrolling and self.scroll > 0

    def can_scroll_down(self):
        return self.scrolling and self.scroll + self.items_per_page() < self.num_items()

    def items_per_page(self):
        return self.num_rows() * self.num_cols()

    def click_cell(self, row, col, event):
        i = self.cell_to_item_no(row, col)
        if i is not None:
            self.click_item(i, event)

    def cell_to_item_no(self, row, col):
        i = self.scroll + row * self.num_cols() + col
        if 0 <= i < self.num_items():
            return i
        else:
            return None

    def num_rows(self):
        ch = self.cell_size[1]
        if ch:
            return self.height // ch
        else:
            return 0

    def num_cols(self):
        width = self.width
        if self.scrolling:
            width -= self.scroll_button_size
        cw = self.cell_size[0]
        if cw:
            return width // cw
        else:
            return 0

    def item_is_selected(self, n):
        return False

    def click_item(self, n, e):
        pass class ThemeProperty(object):

    def __init__(self, name):
        self.name = name
        self.cache_name = intern("_" + name)

    def __get__(self, obj, owner):
        if debug_theme:
            print "%s(%r).__get__(%r)" % (self.__class__.__name__, self.name, obj)
        try:  ###
            cache_name = self.cache_name
            try:
                return getattr(obj, cache_name)
            except AttributeError, e:
                if debug_theme:
                    print e
                value = self.get_from_theme(obj.__class__, self.name)
                obj.__dict__[cache_name] = value
                return value
        except:  ###
            if debug_theme:
                import traceback
                traceback.print_exc()
                print "-------------------------------------------------------"
            raise  ###

    def __set__(self, obj, value):
        if debug_theme:
            print "Setting %r.%s = %r" % (obj, self.cache_name, value)  ###
        obj.__dict__[self.cache_name] = value

    def get_from_theme(self, cls, name):
        return root.get(cls, name)
import os
from pygame import draw, Rect
from pygame.locals import *
from albow.widget import Widget
from albow.dialogs import Dialog, ask, alert
from albow.controls import Label, Button
from albow.fields import TextField
from albow.layout import Row, Column
from albow.palette_view import PaletteView
from albow.theme import ThemeProperty
# -*- coding: utf-8 -*-
#
#   Albow - File Dialogs
#



class DirPathView(Widget):

    def __init__(self, width, client, **kwds):
        Widget.__init__(self, **kwds)
        self.set_size_for_text(width)
        self.client = client

    def draw(self, surf):
        frame = self.get_margin_rect()
        image = self.font.render(self.client.directory, True, self.fg_color)
        tw = image.get_width()
        mw = frame.width
        if tw <= mw:
            x = 0
        else:
            x = mw - tw
        surf.blit(image, (frame.left + x, frame.top))


class FileListView(PaletteView):

    #scroll_button_color = (255, 255, 0)

    def __init__(self, width, client, **kwds):
        font = self.predict_font(kwds)
        h = font.get_linesize()
        d = 2 * self.predict(kwds, 'margin')
        PaletteView.__init__(self, (width - d, h), 10, 1, scrolling=True, **kwds)
        self.client = client
        self.selection = None
        self.names = []

    def update(self):
        client = self.client
        dir = client.directory
        suffixes = client.suffixes

        def filter(name):
            path = os.path.join(dir, name)
            return os.path.isdir(path) or self.client.filter(path)

        try:
            names = [name for name in os.listdir(dir) if filter(name)]
                #if not name.startswith(".") and filter(name)]
        except EnvironmentError, e:
            alert(u"%s: %s" % (dir, e))
            names = []
        self.names = sorted(names)
        self.selection = None
        self.scroll = 0

    def num_items(self):
        return len(self.names)

    #def draw_prehighlight(self, surf, item_no, rect):
    #    draw.rect(surf, self.sel_color, rect)

    def draw_item(self, surf, item_no, rect):
        font = self.font
        color = self.fg_color
        buf = self.font.render(self.names[item_no], True, color)
        surf.blit(buf, rect)

    def click_item(self, item_no, e):
        self.selection = item_no
        self.client.dir_box_click(e.num_clicks == 2)

    def item_is_selected(self, item_no):
        return item_no == self.selection

    def get_selected_name(self):
        sel = self.selection
        if sel is not None:
            return self.names[sel]
        else:
            return ""


class FileDialog(Dialog):

    box_width = 250
    default_prompt = None
    up_button_text = ThemeProperty("up_button_text")

    def __init__(self, prompt=None, suffixes=None, **kwds):

        Dialog.__init__(self, **kwds)