# $Id: Bin.py,v 1.29.2.1 2006/08/17 17:06:33 marcusva Exp $
#
# Copyright (c) 2004-2006, Marcus von Appen
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#  * Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""An abstract widget, which can hold exactly one other widget."""

from BaseWidget import BaseWidget

class Bin (BaseWidget):
    """Bin () -> Bin

    A container widget class, which can hold one other widget.

    The Bin widget class is an abstract class, which can hold exactly
    one other widget. It is usable to serve as a container class, which
    can hold various types of widgets and allows inheritors to use their
    own look.

    The widget to hold can be set or removed using the 'child' attribute
    and set_child() method. The child will not be automatically modified
    by rebinding any of its attributes.
    
    bin.child = widget
    bin.set_child (widget)
    
    The 'padding' attribute and set_padding() method are used to place a
    certain amount of pixels between the child widget and the outer
    edges of the Bin.
    
    bin.padding = 10
    bin.set_padding (10)
    
    Binding the Bin to a new event manager using the 'manager' attribute
    or set_event_manager() method will cause the event manager of the
    child to be set to the same.

    Default action (invoked by activate()):
    None
    
    Mnemonic action (invoked by activate_mnemonic()):
    None
    
    Attributes:
    child   - The widget hold by the Bin.
    padding - Additional padding between the child and outer edges of
              the Bin. Default is 2.
    """
    def __init__ (self):
        BaseWidget.__init__ (self)

        self._child = None
        self._padding = 2

    def set_child (self, child=None):
        """B.set_child (...) -> None

        Sets (or resets) the child of the Bin.

        Creates a parent-child relationship from the Bin to the child by
        associating the Bin with the child and vice versa.

        Raises a TypeError, if the passed argument does not inherit
        from the BaseWidget class.
        Raises an Exception, if the passed argument is already
        attached to another parent.
        """
        self.lock ()
        if child:
            if not isinstance (child, BaseWidget):
                raise TypeError ("child must inherit from BaseWidget")
            if child.parent:
                raise Exception ("child already has a parent")
            child.parent = self

            if (child.depth != self.depth):
                child.set_depth (self.depth)

            if (self.manager != None) and not child.manager:
                child.set_event_manager (self.manager)

            # Set the states for the child.
            if not self.sensitive:
                child.set_sensitive (self.sensitive)
        if self._child:
            self._child.parent = None
        self._child = child
        self.dirty = True
        self.unlock ()

    def set_depth (self, depth):
        """B.set_depth (...) -> None

        Sets the depth of the Bin.

        Sets the depth of the Bin and its child to the given value.
        """
        self.lock ()
        BaseWidget.set_depth (self, depth)
        if self.child:
            self.child.set_depth (depth)
        self.unlock ()

    def set_indexable (self, indexable):
        """B.set_indexable (...) -> None

        Sets the indexable of the Bin.

        Adds the Bin to an IIndexable implementation and causes its child
        to be added to the same, too.
        """
        BaseWidget.set_indexable (self, indexable)
        if self.child:
            self.child.set_indexable (indexable)

    def set_event_manager (self, manager):
        """B.set_event_manager (...) -> None

        Sets the event manager of the Bin.

        Adds the Bin to an event manager and causes its child to be
        added to the same, too.
        """
        BaseWidget.set_event_manager (self, manager)
        if self.child:
            self.child.set_event_manager (manager)

    def set_sensitive (self, sensitive=True):
        """B.set_sensitive (...) -> None

        Sets the sensitivity of the Bin and its child.
        """
        self.lock ()
        BaseWidget.set_sensitive (self, sensitive)
        if self.child:
            self.child.set_sensitive (sensitive)
        self.unlock ()
    
    def set_padding (self, padding):
        """B.set_padding (...) -> None

        Sets the padding between the child and edges of the Bin.

        The padding value is the amount of pixels to place between the
        edges of the Bin and the contained child.

        Raises a TypeError, if the passed argument is not a positive
        integer.
        """
        if (type (padding) != int) or (padding < 0):
            raise TypeError ("padding must be a positive integer")
        self._padding = padding
        self.dirty = True

    def destroy (self):
        """B.destroy () -> None

        Destroys the Bin and removes it from its event system.
        """
        if self.child:
            w = self.child
            w.parent = None
            self.child = None
            w.destroy ()
            del w
        BaseWidget.destroy (self)

    def update (self, **kwargs):
        """B.update (...) -> None

        Updates the Bin and refreshes its image and rect content.

        Updates the Bin and causes its parent to update itself on
        demand.
        """
        children = kwargs.get ("children", {})
        resize = kwargs.get ("resize", False)

        if self.locked:
            return
        # We have to check for possible size changes here!
        if resize:
            self.dirty = True
        else:
            BaseWidget.update (self, children=children, resize=resize)
    
    child = property (lambda self: self._child,
                      lambda self, var: self.set_child (var),
                      doc = "The widget hold by the Bin.")
    padding = property (lambda self: self._padding,
                        lambda self, var: self.set_padding (var),
                        doc = "Additional padding between child and borders.")
