#
#   Python GUI - Components - Macintosh
#

from Carbon.Qd import NewRgn
from Properties import overridable_property
from Geometry import sub_pt, rect_topleft, rect_size
from GComponents import Component as GComponent

class Component(GComponent):

    #  _visible        boolean
    
    border = overridable_property('border', "Whether to draw a border around this Component.")

    _border = 0
    _mac_halo = 0
    _next_dispatcher = None

    def __init__(self, **kwds):
        self._visible = 1
        self._window = None
        self._mac_clip_rgn = NewRgn()
        GComponent.__init__(self, **kwds)
    
    #
    #		Properties
    #

    def get_left(self):
        return self.bounds[0]

    def set_left(self, v):
        l, t, r, b = self.bounds
        self.bounds = (v, t, r, b)

    def get_top(self):
        return self.bounds[1]

    def set_top(self, v):
        l, t, r, b = self.bounds
        self.bounds = (l, v, r, b)
        
    def get_right(self):
        return self.bounds[2]
    
    def set_right(self, v):
        l, t, r, b = self.bounds
        self.bounds = (l, t, v, b)
    
    def get_bottom(self):
        return self.bounds[3]
    
    def set_bottom(self, v):
        l, t, r, b = self.bounds
        self.bounds = (l, t, r, v)
        
    get_x = get_left

    def set_x(self, v):
        l, t, r, b = self.bounds
        self.bounds = (v, t, v + r - l, b)

    get_y = get_top

    def set_y(self, v):
        l, t, r, b = self.bounds
        self.bounds = (l, v, r, v + b - t)

    def get_width(self):
        return self.size[0]
 
    def set_width(self, v):
        l, t, r, b = self.bounds
        self.bounds = (l, t, l + v, b)

    def get_height(self):
        return self.size[1]
    
    def set_height(self, v):
        l, t, r, b = self.bounds
        self.bounds = (l, t, r, t + v)

    def get_position(self):
        return rect_topleft(self.bounds)
    
    def set_position(self, (x, y)):
        l, t, r, b = self.bounds
        self.bounds = (x, y, x + r - l, y + b - t)

    def get_size(self):
        return rect_size(self.bounds)
    
    def set_size(self, (w, h)):
        l, t, r, b = self.bounds
        self.bounds = (l, t, l + w, t + h)

#	def set_bounds(self, new_bounds):
#		old_bounds = self.get_bounds()
#		if new_bounds <> old_bounds:
#			self._change_bounds(new_bounds)
#			delta = sub_pt(rect_size(new_bounds), rect_size(old_bounds))
#			if delta <> (0, 0):
#				self.resized(delta)
    
    def set_bounds(self, new_bounds):
        self._change_bounds(new_bounds)
    
    def get_visible(self):
        return self._visible
    
    def set_visible(self, new_v):
        cur_v = self.visible
        if new_v and not cur_v:
            self._mac_show()
        elif cur_v and not new_v:
            self._mac_hide()
    
    def _change_container(self, new_container):
        was_target = self.is_target()
        GComponent._change_container(self, new_container)
        now_target = self.is_target()
        if was_target <> now_target:
            self.targeted(now_target)

    def get_border(self):
        return self._border
    
    def set_border(self, v):
        if self._border <> v:
            self._border = v
            if self.visible:
                self._invalidate_in_container()

    #
    #		Message dispatching
    #
    
    def dispatch(self, message, *args):
        """Pass the message on to the next_target, or handle it if
        there is no next_target."""
        #if message == 'key_down': ###
        #	print "GMessageHandler.dispatch:", self, message, args ###
        t = self._next_dispatcher
        if t:
            t.dispatch(message, *args)
        else:
            self.handle(message, *args)

    def reverse_dispatch(self, message, *args):
        """Walk down the next_target chain calling all handlers for the
        given message in order from top to bottom."""
        method = getattr(self, message, None)
        if method:
            method(*args)
        t = self._next_dispatcher
        if t:
            t.reverse_dispatch(message, *args)
    
    def first_dispatcher(self):
        """Return the topmost object in the dispatching hierarchy."""
        prev = self.previous_dispatcher()
        if prev:
            return prev.first_dispatcher()
        else:
            return None
    
    def last_dispatcher(self):
        """Return the bottommost object in the dispatcher chain starting
        from this point in the hierarchy."""
        next = self._next_dispatcher
        if next:
            return next.last_dispatcher()
        else:
            return self
    
    def _dispatch_to(self, obj):
        """Arrange for messages to be dispatched via this object
        to the given object."""
        #print "GMessageHandler._dispatch_to:", self, obj ###
        prev = self.previous_dispatcher()
        if prev:
            prev._dispatch_to(self)
        self._next_dispatcher = obj

    def _dispatches_to(self, obj):
        """Return true if this object is currently on the dispatching path
        and is dispatching to the given object."""
        if self._next_dispatcher is not obj:
            return 0
        prev = self.previous_dispatcher()
        return not prev or prev._dispatches_to(self)
    
    def previous_dispatcher(self):
        """Return the next higher object, if any, in the dispatching
        hierarchy."""
        return self._container
    
    #
    #   Target setting and querying
    #

    def become_target(self):
        """Arrange for this object to be the first to handle messages
        dispatched from the top of the hierarchy."""
        current = self.current_target()
        if current is not self:
            if current:
                current.targeted(0)
            self._dispatch_to(None)
            self.targeted(1)
    
    def current_target(self):
        """Find the current target object in the dispatching hierarchy
        to which this object belongs."""
        top = self.first_dispatcher()
        if top:
            return top.last_dispatcher()
        else:
            return None
    
    def is_target(self):
        """Return true if this is the current dispatching target."""
        return self._dispatches_to(None)
    
    #
    #   Internal
    #

    def _mac_show(self):
        self._visible = 1
        self._mac_update_viewport()
        self._invalidate_in_container()
    
    def _mac_hide(self):
        self._invalidate_in_container()
        self._visible = 0
        self._mac_update_viewport()
    
    def _mac_update_viewport(self):
        pass

    def _change_bounds(self, v):
        visible = self._visible
        if visible:
            self._invalidate_in_container()
        self._mac_component_change_bounds(v)
        if visible:
            self._invalidate_in_container()

    def _with_focus(self, proc, *args):
        # Focus the grafport on this component, execute the
        # procedure, and then restore the previous focus.
        container = self._container
        if container:
            container._with_focus(proc, *args)
        else:
            proc(*args)

    def _mac_inval_rect(self, rect):
        # Invalidate the given region.
        # NOTE: Must be called inside _with_focus.
        container = self._container
        if container:
            container._mac_inval_rect(rect)

    def _mac_invalidate(self):
        # Invalidate the whole component.
        # NOTE: Must be called inside _with_focus.
        self._mac_inval_rect(self.bounds)
