#
#  GraphicsView.py
#  GraphicsBindings
#
#  Converted by u.fiedler on feb 2005
#  with great help from Bob Ippolito - Thank you Bob!
#
#  The original version was written in Objective-C by Malcolm Crawford
#  http://homepage.mac.com/mmalc/CocoaExamples/controllers.html

PropertyObservationContext = 1091
GraphicsObservationContext = 1092
SelectionIndexesObservationContext = 1093


from Foundation import *
from AppKit import *
from objc import ivar
from Circle import Circle
from sets import Set

class GraphicsView(NSView):
    graphicsContainer = ivar(u'graphicsContainer')
    graphicsKeyPath   = ivar(u'graphicsKeyPath')

    selectionIndexesContainer = ivar(u'selectionIndexesContainer') # GraphicsArrayController
    selectionIndexesKeyPath   = ivar(u'selectionIndexesKeyPath')

    oldGraphics = ivar(u'oldGraphics')

    def exposedBindings(self):
        return [u"graphics", u"selectedObjects"]

    def initWithFrame_(self, frameRect):
        return super(GraphicsView, self).initWithFrame_(frameRect)

    def graphics(self):
        if not self.graphicsContainer: return None
        return self.graphicsContainer.valueForKeyPath_(self.graphicsKeyPath)

    def selectionIndexes(self):
        if not self.selectionIndexesContainer: return None
        return self.selectionIndexesContainer.valueForKeyPath_(self.selectionIndexesKeyPath)

    def startObservingGraphics_(self, graphics):
        if not graphics: return
        # Register to observe each of the new graphics, and
        # each of their observable properties -- we need old and new
        # values for drawingBounds to figure out what our dirty rect
        for newGraphic in graphics:
            # Register as observer for all the drawing-related properties
            newGraphic.addObserver_forKeyPath_options_context_(
                self, u"drawingBounds", (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld),
                PropertyObservationContext)
            keys = Circle.keysForNonBoundsProperties()
            for key in keys:
                newGraphic.addObserver_forKeyPath_options_context_(
                    self, key, 0, PropertyObservationContext)

    def stopObservingGraphics_(self, graphics):
        if graphics is None: return
        for graphic in graphics:
            for key in graphic.class__().keysForNonBoundsProperties():
                graphic.removeObserver_forKeyPath_(self, key)
            graphic.removeObserver_forKeyPath_(self, u"drawingBounds")

    def bind_toObject_withKeyPath_options_(self, bindingName, observableObject, observableKeyPath, options):
        if bindingName == u"graphics":
            self.graphicsContainer = observableObject
            self.graphicsKeyPath = observableKeyPath
            self.graphicsContainer.addObserver_forKeyPath_options_context_(
                    self, self.graphicsKeyPath, (NSKeyValueObservingOptionNew |
                    NSKeyValueObservingOptionOld), GraphicsObservationContext)
            self.startObservingGraphics_(self.graphics())

        elif bindingName == u"selectionIndexes":
            self.selectionIndexesContainer = observableObject
            self.selectionIndexesKeyPath = observableKeyPath
            self.selectionIndexesContainer.addObserver_forKeyPath_options_context_(
                self, self.selectionIndexesKeyPath, 0, SelectionIndexesObservationContext)
        self.setNeedsDisplay_(True)

    def unbind_(self, bindingName):
        if bindingName == u"graphics":
            self.graphicsContainer.removeObserver_forKeyPath_(self, self.graphicsKeyPath)
            self.graphicsContainer = None
            self.graphicsKeyPath = None
        if bindingName == u"selectionIndexes":
            self.selectionIndexesContainer.removeObserver_forKeyPath_(self, self.selectionIndexesKeyPath)
            self.seletionIndexesContainer = None
            self.selectionIndexesKeyPath = None
        self.setNeedsDisplay_(True)

    def observeValueForKeyPath_ofObject_change_context_(self, keyPath, object, change, context):
        if context == GraphicsObservationContext:
            # Should be able to use
            # NSArray *oldGraphics = [change objectForKey:NSKeyValueChangeOldKey];
            # etc. but the dictionary doesn't contain old and new arrays...??
            newGraphics = Set(object.valueForKeyPath_(self.graphicsKeyPath))
            onlyNew = newGraphics - Set(self.oldGraphics)
            self.startObservingGraphics_(onlyNew)

            if self.oldGraphics:
                removed = Set(self.oldGraphics) - newGraphics
                self.stopObservingGraphics_(removed)

            self.oldGraphics = newGraphics

            # could check drawingBounds of old and new, but...
            self.setNeedsDisplay_(True)
            return

        if context == PropertyObservationContext:
            updateRect = (0,)
            # Note: for Circle, drawingBounds is a dependent key of all the other
            # property keys except color, so we'll get this anyway...
            if keyPath == u"drawingBounds":
                newBounds = change.objectForKey_(NSKeyValueChangeNewKey)
                oldBounds = change.objectForKey_(NSKeyValueChangeOldKey)
                updateRect = NSUnionRect(newBounds, oldBounds)
            else:
                updateRect = object.drawingBounds()
            updateRect = NSMakeRect(updateRect.origin.x-1.0,
                                updateRect.origin.y-1.0,
                                updateRect.size.width+2.0,
                                updateRect.size.height+2.0)
            self.setNeedsDisplay_(True)
            return

        if context == SelectionIndexesObservationContext:
            self.setNeedsDisplay_(True)
            return

    def drawRect_(self, rect):
        myBounds = self.bounds()
        NSDrawLightBezel(myBounds, myBounds) # AppKit Function
        clipRect = NSBezierPath.bezierPathWithRect_(NSInsetRect(myBounds, 2.0, 2.0))
        clipRect.addClip()

        # Draw graphics
        graphicsArray = self.graphics()
        if graphicsArray:
            for graphic in graphicsArray:
                graphicDrawingBounds = graphic.drawingBounds()
                if NSIntersectsRect(rect, graphicDrawingBounds):
                    graphic.drawInView_(self)

        # Draw a red box around items in the current selection.
        # Selection should be handled by the graphic, but this is a
        # shortcut simply for display.

        currentSelectionIndexes = self.selectionIndexes() # ist das wir ein Array im Indezes?
        if currentSelectionIndexes != None:
            path = NSBezierPath.bezierPath()
            index = currentSelectionIndexes.firstIndex()
            while index != NSNotFound:
                graphicDrawingBounds = graphicsArray[index].drawingBounds()
                if NSIntersectsRect(rect, graphicDrawingBounds):
                    path.appendBezierPathWithRect_(graphicDrawingBounds)
                index = currentSelectionIndexes.indexGreaterThanIndex_(index)

            NSColor.redColor().set()
            path.setLineWidth_(1.5)
            path.stroke()


        # Fairly simple just to illustrate the point
    def mouseDown_(self, event):
        # find out if we hit anything
        p = self.convertPoint_fromView_(event.locationInWindow(), None)
        for aGraphic in self.graphics():
            if aGraphic.hitTest_isSelected_(p, False):
                break; # aGraphic soll spaeter einen Wert haben, falls es getroffene gibt!
        else:
            aGraphic = None

        # if no graphic hit, then if extending selection do nothing
        # else set selection to nil
        if aGraphic == None:
            if not event.modifierFlags() & NSShiftKeyMask:
                self.selectionIndexesContainer.setValue_forKeyPath_(None, self.selectionIndexesKeyPath)
            return

        # graphic hit
        # if not extending selection (Shift key down) then set
        # selection to this graphic
        # if extending selection, then:
        # - if graphic in selection remove it
        # - if not in selection add it
        graphicIndex = self.graphics().index(aGraphic)
        if not event.modifierFlags() & NSShiftKeyMask:
            selection = NSIndexSet.indexSetWithIndex_(graphicIndex)
        else:
            if  self.selectionIndexes().containsIndex_(graphicIndex):
                selection = self.selectionIndexes().mutableCopy()
                selection.removeIndex_(graphicIndex)
            else:
                selection = self.selectionIndexes().mutableCopy()
                selection.addIndex_(graphicIndex)
        self.selectionIndexesContainer.setValue_forKeyPath_(selection, self.selectionIndexesKeyPath)


GraphicsView.exposeBinding_(u"graphics")
GraphicsView.exposeBinding_(u"selectionIndexes")
