#
#   Python GUI - Views - Macintosh
#

from Carbon.Qd import NewRgn, RectRgn, XorRgn, SectRgn, ScrollRect
from Properties import overridable_property
from Applications import application
from GViews import View as GView, _default_line_scroll_amount
from Containers import Container
from Canvases import Canvas

class View(GView):

    #  _extent               rect
    #  _line_scroll_amount   point

    def __init__(self, **kwds):
        self._extent = (0, 0, 0, 0)
        self._scroll_offset = (0, 0)
        self._line_scroll_amount = _default_line_scroll_amount
        GView.__init__(self, **kwds)

    def destroy(self):	
        for b in self._scroll_bars[:]:
            b.client = None
        GView.destroy(self)

    #
    #		Getting properties
    #
    
    def get_extent(self):
        return self._extent
    
    def get_scroll_offset(self):
        return self._scroll_offset

    def get_scroll_bars(self):
        return self._scroll_bars
    
    def get_line_scroll_amount(self):
        return self._line_scroll_amount

    #
    #		Setting properties
    #

    def set_extent(self, new_extent):
        old_extent = self._extent
        if new_extent <> old_extent:
            self._extent = new_extent
            self._extent_changed(old_extent, new_extent)
            self._change_scroll_offset(self._scroll_offset)
            self._update_scroll_bars()

    def set_scroll_offset(self, p):
        """Set the scroll offset."""
        if self._change_scroll_offset(p):
            self._update_scroll_bars()
    
    def set_line_scroll_amount(self, p):
        self._line_scroll_amount = p
    
    #
    #		Scrolling
    #

    def scroll_to_h(self, f):
        """Called by horizontal scroll bar to scroll to the
        specified fraction, in range 0.0 - 1.0."""
        y = self._scroll_offset[1]
        l, t, r, b = self._extent
        x = l + f * (r - l - self.width)
        self.scroll_offset = (x, y)

    def scroll_to_v(self, f):
        """Called by vertical scroll bar to scroll to the
        specified fraction, in range 0.0 - 1.0."""
        x = self._scroll_offset[0]
        l, t, r, b = self._extent
        y = t + f * (b - t - self.height)
        self.scroll_offset = (x, y)

    def h_scroll_bar_value(self):
        """Calculate horizontal scroll bar value and proportion, in range 0.0 - 1.0."""
        x = self._scroll_offset[0]
        l, t, r, b = self._extent
        w = self.width
        return float(x - l) / float(r - l - w), float(w) / float(r - l)

    def v_scroll_bar_value(self):
        """Calculate vertical scroll bar value and proportion, in range 0.0 - 1.0."""
        y = self._scroll_offset[1]
        l, t, r, b = self._extent
        h = self.height
        return float(y - t) / float(b - t - h), float(h) / float(b - t)

    #
    #		Other methods
    #

    def with_canvas(self, proc):
        # Call the procedure with a canvas for this component,
        # making no assumptions about the drawing state.
        # Restores the previous focus and graphics state afterwards.
        self._with_focus(lambda: self._with_canvas(proc))

    #
    #		Internal
    #

    def _scroll_offset_changed(self):
        self._mac_update_viewport()

    def _extent_changed(self, old_extent, new_extent):
        rgn1 = NewRgn()
        rgn2 = NewRgn()
        RectRgn(rgn1, old_extent)
        RectRgn(rgn2, new_extent)
        XorRgn(rgn1, rgn2, rgn2)
        self._with_focus(self._mac_inval_rgn, rgn2)

    def _scroll_rect(self, rect, dx, dy):
        #print "View._scroll_rect:", rect, "by", (dx, dy) ###
        self._with_focus(self._mac_scroll_rect, rect, dx, dy)
    
    def _mac_scroll_rect(self, rect, dx, dy):
        #print "View._mac_scroll_rect:", rect, "by", (dx, dy) ###
        update_rgn = NewRgn()
        ScrollRect(rect, dx, dy, update_rgn)
        #from Carbon.Qd import GetRegionBounds, PaintRgn ###
        #print "...update_rgn box =", GetRegionBounds(update_rgn) ###
        #PaintRgn(update_rgn) ###
        self._mac_inval_rgn(update_rgn)

    def _mac_draw_self(self):
        # Draw the user-defined contents,
        # assuming the grafport is focused on this component.
        self._with_canvas(self.draw)
    
    def _with_canvas(self, proc):
        # Call the procedure with a canvas for this component,
        # assuming the grafport is focused on this component.
        # Restores the previous graphics state afterwards.
        canvas = Canvas(self._mac_vis_rgn)
        canvas.gsave()
        canvas.initgraphics()
        try:
            proc(canvas)
        finally:
            canvas.grestore()
    
    def _change_scroll_offset(self, p):
        # Change the current scroll offset to the specified value,
        # limited by the current extent, scrolling the view's contents
        # on screen and invalidating exposed areas as necessary. Returns
        # true if any scrolling was actually performed.
        #print "View._change_scroll_offset:", p ###
        nx, ny = p
        nx = int(round(nx))
        ny = int(round(ny))
        ox, oy = self._scroll_offset
        l, t, r, b = self._extent
        w = self.width
        h = self.height
        r = r - w
        b = b - h
        if nx > r:
            nx = r
        if nx < l:
            nx = l
        if ny > b:
            ny = b
        if ny < t:
            ny = t
        if nx <> ox or ny <> oy:
            dx = nx - ox
            dy = ny - oy
            #print "...delta =", (dx, dy) ###
            if abs(dx) < w and abs(dy) < h:
                self._scroll_rect(self.viewed_rect(), -dx, -dy)
            else:
                #print "...invalidating whole view" ###
                self.invalidate()
            self._scroll_offset = (nx, ny)
            self._scroll_offset_changed()
            return 1
        else:
            return 0

    def _scroll_offset_changed(self):
        self._mac_update_viewport()

    def _update_scroll_bars(self):
        # Tell scroll bars to update themselves after scrolling has occurred.
        for bar in self._scroll_bars:
            bar.update()

