1# 2# GraphicsView.py 3# GraphicsBindings 4# 5# Converted by u.fiedler on feb 2005 6# with great help from Bob Ippolito - Thank you Bob! 7# 8# The original version was written in Objective-C by Malcolm Crawford 9# http://homepage.mac.com/mmalc/CocoaExamples/controllers.html 10 11PropertyObservationContext = 1091 12GraphicsObservationContext = 1092 13SelectionIndexesObservationContext = 1093 14 15 16from Foundation import * 17from AppKit import * 18from objc import ivar 19from Circle import Circle 20from sets import Set 21 22class GraphicsView(NSView): 23 graphicsContainer = ivar(u'graphicsContainer') 24 graphicsKeyPath = ivar(u'graphicsKeyPath') 25 26 selectionIndexesContainer = ivar(u'selectionIndexesContainer') # GraphicsArrayController 27 selectionIndexesKeyPath = ivar(u'selectionIndexesKeyPath') 28 29 oldGraphics = ivar(u'oldGraphics') 30 31 def exposedBindings(self): 32 return [u"graphics", u"selectedObjects"] 33 34 def initWithFrame_(self, frameRect): 35 return super(GraphicsView, self).initWithFrame_(frameRect) 36 37 def graphics(self): 38 if not self.graphicsContainer: return None 39 return self.graphicsContainer.valueForKeyPath_(self.graphicsKeyPath) 40 41 def selectionIndexes(self): 42 if not self.selectionIndexesContainer: return None 43 return self.selectionIndexesContainer.valueForKeyPath_(self.selectionIndexesKeyPath) 44 45 def startObservingGraphics_(self, graphics): 46 if not graphics: return 47 # Register to observe each of the new graphics, and 48 # each of their observable properties -- we need old and new 49 # values for drawingBounds to figure out what our dirty rect 50 for newGraphic in graphics: 51 # Register as observer for all the drawing-related properties 52 newGraphic.addObserver_forKeyPath_options_context_( 53 self, u"drawingBounds", (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 54 PropertyObservationContext) 55 keys = Circle.keysForNonBoundsProperties() 56 for key in keys: 57 newGraphic.addObserver_forKeyPath_options_context_( 58 self, key, 0, PropertyObservationContext) 59 60 def stopObservingGraphics_(self, graphics): 61 if graphics is None: return 62 for graphic in graphics: 63 for key in graphic.class__().keysForNonBoundsProperties(): 64 graphic.removeObserver_forKeyPath_(self, key) 65 graphic.removeObserver_forKeyPath_(self, u"drawingBounds") 66 67 def bind_toObject_withKeyPath_options_(self, bindingName, observableObject, observableKeyPath, options): 68 if bindingName == u"graphics": 69 self.graphicsContainer = observableObject 70 self.graphicsKeyPath = observableKeyPath 71 self.graphicsContainer.addObserver_forKeyPath_options_context_( 72 self, self.graphicsKeyPath, (NSKeyValueObservingOptionNew | 73 NSKeyValueObservingOptionOld), GraphicsObservationContext) 74 self.startObservingGraphics_(self.graphics()) 75 76 elif bindingName == u"selectionIndexes": 77 self.selectionIndexesContainer = observableObject 78 self.selectionIndexesKeyPath = observableKeyPath 79 self.selectionIndexesContainer.addObserver_forKeyPath_options_context_( 80 self, self.selectionIndexesKeyPath, 0, SelectionIndexesObservationContext) 81 self.setNeedsDisplay_(True) 82 83 def unbind_(self, bindingName): 84 if bindingName == u"graphics": 85 self.graphicsContainer.removeObserver_forKeyPath_(self, self.graphicsKeyPath) 86 self.graphicsContainer = None 87 self.graphicsKeyPath = None 88 if bindingName == u"selectionIndexes": 89 self.selectionIndexesContainer.removeObserver_forKeyPath_(self, self.selectionIndexesKeyPath) 90 self.seletionIndexesContainer = None 91 self.selectionIndexesKeyPath = None 92 self.setNeedsDisplay_(True) 93 94 def observeValueForKeyPath_ofObject_change_context_(self, keyPath, object, change, context): 95 if context == GraphicsObservationContext: 96 # Should be able to use 97 # NSArray *oldGraphics = [change objectForKey:NSKeyValueChangeOldKey]; 98 # etc. but the dictionary doesn't contain old and new arrays...?? 99 newGraphics = Set(object.valueForKeyPath_(self.graphicsKeyPath)) 100 onlyNew = newGraphics - Set(self.oldGraphics) 101 self.startObservingGraphics_(onlyNew) 102 103 if self.oldGraphics: 104 removed = Set(self.oldGraphics) - newGraphics 105 self.stopObservingGraphics_(removed) 106 107 self.oldGraphics = newGraphics 108 109 # could check drawingBounds of old and new, but... 110 self.setNeedsDisplay_(True) 111 return 112 113 if context == PropertyObservationContext: 114 updateRect = (0,) 115 # Note: for Circle, drawingBounds is a dependent key of all the other 116 # property keys except color, so we'll get this anyway... 117 if keyPath == u"drawingBounds": 118 newBounds = change.objectForKey_(NSKeyValueChangeNewKey) 119 oldBounds = change.objectForKey_(NSKeyValueChangeOldKey) 120 updateRect = NSUnionRect(newBounds, oldBounds) 121 else: 122 updateRect = object.drawingBounds() 123 updateRect = NSMakeRect(updateRect.origin.x-1.0, 124 updateRect.origin.y-1.0, 125 updateRect.size.width+2.0, 126 updateRect.size.height+2.0) 127 self.setNeedsDisplay_(True) 128 return 129 130 if context == SelectionIndexesObservationContext: 131 self.setNeedsDisplay_(True) 132 return 133 134 def drawRect_(self, rect): 135 myBounds = self.bounds() 136 NSDrawLightBezel(myBounds, myBounds) # AppKit Function 137 clipRect = NSBezierPath.bezierPathWithRect_(NSInsetRect(myBounds, 2.0, 2.0)) 138 clipRect.addClip() 139 140 # Draw graphics 141 graphicsArray = self.graphics() 142 if graphicsArray: 143 for graphic in graphicsArray: 144 graphicDrawingBounds = graphic.drawingBounds() 145 if NSIntersectsRect(rect, graphicDrawingBounds): 146 graphic.drawInView_(self) 147 148 # Draw a red box around items in the current selection. 149 # Selection should be handled by the graphic, but this is a 150 # shortcut simply for display. 151 152 currentSelectionIndexes = self.selectionIndexes() # ist das wir ein Array im Indezes? 153 if currentSelectionIndexes != None: 154 path = NSBezierPath.bezierPath() 155 index = currentSelectionIndexes.firstIndex() 156 while index != NSNotFound: 157 graphicDrawingBounds = graphicsArray[index].drawingBounds() 158 if NSIntersectsRect(rect, graphicDrawingBounds): 159 path.appendBezierPathWithRect_(graphicDrawingBounds) 160 index = currentSelectionIndexes.indexGreaterThanIndex_(index) 161 162 NSColor.redColor().set() 163 path.setLineWidth_(1.5) 164 path.stroke() 165 166 167 # Fairly simple just to illustrate the point 168 def mouseDown_(self, event): 169 # find out if we hit anything 170 p = self.convertPoint_fromView_(event.locationInWindow(), None) 171 for aGraphic in self.graphics(): 172 if aGraphic.hitTest_isSelected_(p, False): 173 break; # aGraphic soll spaeter einen Wert haben, falls es getroffene gibt! 174 else: 175 aGraphic = None 176 177 # if no graphic hit, then if extending selection do nothing 178 # else set selection to nil 179 if aGraphic == None: 180 if not event.modifierFlags() & NSShiftKeyMask: 181 self.selectionIndexesContainer.setValue_forKeyPath_(None, self.selectionIndexesKeyPath) 182 return 183 184 # graphic hit 185 # if not extending selection (Shift key down) then set 186 # selection to this graphic 187 # if extending selection, then: 188 # - if graphic in selection remove it 189 # - if not in selection add it 190 graphicIndex = self.graphics().index(aGraphic) 191 if not event.modifierFlags() & NSShiftKeyMask: 192 selection = NSIndexSet.indexSetWithIndex_(graphicIndex) 193 else: 194 if self.selectionIndexes().containsIndex_(graphicIndex): 195 selection = self.selectionIndexes().mutableCopy() 196 selection.removeIndex_(graphicIndex) 197 else: 198 selection = self.selectionIndexes().mutableCopy() 199 selection.addIndex_(graphicIndex) 200 self.selectionIndexesContainer.setValue_forKeyPath_(selection, self.selectionIndexesKeyPath) 201 202 203GraphicsView.exposeBinding_(u"graphics") 204GraphicsView.exposeBinding_(u"selectionIndexes") 205