1#import "CPTGraph.h" 2#import "CPTExceptions.h" 3#import "CPTLegend.h" 4#import "CPTPlot.h" 5#import "CPTPlotArea.h" 6#import "CPTPlotAreaFrame.h" 7#import "CPTMutableTextStyle.h" 8#import "CPTPlotSpace.h" 9#import "CPTFill.h" 10#import "CPTAxisSet.h" 11#import "CPTAxis.h" 12#import "CPTTheme.h" 13#import "CPTLayerAnnotation.h" 14#import "CPTTextLayer.h" 15#import "NSCoderExtensions.h" 16 17/** @defgroup graphAnimation Graphs 18 * @brief Graph properties that can be animated using Core Animation. 19 * @if MacOnly 20 * @since Custom layer property animation is supported on MacOS 10.6 and later. 21 * @endif 22 * @ingroup animation 23 **/ 24 25NSString * const CPTGraphNeedsRedrawNotification = @"CPTGraphNeedsRedrawNotification"; 26 27/** @cond */ 28@interface CPTGraph() 29 30@property (nonatomic, readwrite, retain) NSMutableArray *plots; 31@property (nonatomic, readwrite, retain) NSMutableArray *plotSpaces; 32@property (nonatomic, readwrite, retain) CPTLayerAnnotation *titleAnnotation; 33@property (nonatomic, readwrite, retain) CPTLayerAnnotation *legendAnnotation; 34 35-(void)plotSpaceMappingDidChange:(NSNotification *)notif; 36-(CGPoint)contentAnchorForLegend; 37 38@end 39/** @endcond */ 40 41#pragma mark - 42 43/** @brief An abstract graph class. 44 * @todo More documentation needed 45 **/ 46@implementation CPTGraph 47 48/** @property axisSet 49 * @brief The axis set. 50 **/ 51@dynamic axisSet; 52 53/** @property plotAreaFrame 54 * @brief The plot area frame. 55 **/ 56@synthesize plotAreaFrame; 57 58/** @property plots 59 * @brief An array of all plots associated with the graph. 60 **/ 61@synthesize plots; 62 63/** @property plotSpaces 64 * @brief An array of all plot spaces associated with the graph. 65 **/ 66@synthesize plotSpaces; 67 68/** @property defaultPlotSpace 69 * @brief The default plot space. 70 **/ 71@dynamic defaultPlotSpace; 72 73/** @property topDownLayerOrder 74 * @brief An array of graph layers to be drawn in an order other than the default. 75 * @see CPTPlotArea#topDownLayerOrder 76 **/ 77@dynamic topDownLayerOrder; 78 79/** @property title 80 * @brief The title string. 81 * Default is nil. 82 **/ 83@synthesize title; 84 85/** @property titleTextStyle 86 * @brief The text style of the title. 87 **/ 88@synthesize titleTextStyle; 89 90/** @property titlePlotAreaFrameAnchor 91 * @brief The location of the title with respect to the plot area frame. 92 * Default is top center. 93 **/ 94@synthesize titlePlotAreaFrameAnchor; 95 96/** @property titleDisplacement 97 * @brief A vector giving the displacement of the title from the edge location. 98 * @ingroup graphAnimation 99 **/ 100@synthesize titleDisplacement; 101 102/** @property legend 103 * @brief The graph legend. 104 * Setting this property will automatically anchor the legend to the graph and position it 105 * using the legendAnchor and legendDisplacement properties. This is a convenience property 106 * only—the legend may be inserted in the layer tree and positioned like any other CPTLayer 107 * if more flexibility is needed. 108 **/ 109@dynamic legend; 110 111/** @property legendAnchor 112 * @brief The location of the legend with respect to the graph frame. 113 * Default is bottom center. 114 **/ 115@synthesize legendAnchor; 116 117/** @property legendDisplacement 118 * @brief A vector giving the displacement of the legend from the edge location. 119 * @ingroup graphAnimation 120 **/ 121@synthesize legendDisplacement; 122 123@synthesize titleAnnotation; 124@synthesize legendAnnotation; 125 126#pragma mark - 127#pragma mark Init/Dealloc 128 129-(id)initWithFrame:(CGRect)newFrame 130{ 131 if ( (self = [super initWithFrame:newFrame]) ) { 132 plots = [[NSMutableArray alloc] init]; 133 134 // Margins 135 self.paddingLeft = 20.0; 136 self.paddingTop = 20.0; 137 self.paddingRight = 20.0; 138 self.paddingBottom = 20.0; 139 140 // Plot area 141 CPTPlotAreaFrame *newArea = [(CPTPlotAreaFrame *)[CPTPlotAreaFrame alloc] initWithFrame:self.bounds]; 142 self.plotAreaFrame = newArea; 143 [newArea release]; 144 145 // Plot spaces 146 plotSpaces = [[NSMutableArray alloc] init]; 147 CPTPlotSpace *newPlotSpace = [self newPlotSpace]; 148 [self addPlotSpace:newPlotSpace]; 149 [newPlotSpace release]; 150 151 // Axis set 152 CPTAxisSet *newAxisSet = [self newAxisSet]; 153 self.axisSet = newAxisSet; 154 [newAxisSet release]; 155 156 // Title 157 title = nil; 158 titlePlotAreaFrameAnchor = CPTRectAnchorTop; 159 titleTextStyle = [[CPTTextStyle textStyle] retain]; 160 titleDisplacement = CGPointZero; 161 titleAnnotation = nil; 162 163 // Legend 164 legend = nil; 165 legendAnnotation = nil; 166 legendAnchor = CPTRectAnchorBottom; 167 legendDisplacement = CGPointZero; 168 169 self.needsDisplayOnBoundsChange = YES; 170 } 171 return self; 172} 173 174-(id)initWithLayer:(id)layer 175{ 176 if ( (self = [super initWithLayer:layer]) ) { 177 CPTGraph *theLayer = (CPTGraph *)layer; 178 179 plotAreaFrame = [theLayer->plotAreaFrame retain]; 180 plots = [theLayer->plots retain]; 181 plotSpaces = [theLayer->plotSpaces retain]; 182 title = [theLayer->title retain]; 183 titlePlotAreaFrameAnchor = theLayer->titlePlotAreaFrameAnchor; 184 titleTextStyle = [theLayer->titleTextStyle retain]; 185 titleDisplacement = theLayer->titleDisplacement; 186 titleAnnotation = [theLayer->titleAnnotation retain]; 187 legend = [theLayer->legend retain]; 188 legendAnnotation = [theLayer->legendAnnotation retain]; 189 legendAnchor = theLayer->legendAnchor; 190 legendDisplacement = theLayer->legendDisplacement; 191 } 192 return self; 193} 194 195-(void)dealloc 196{ 197 [[NSNotificationCenter defaultCenter] removeObserver:self]; 198 199 [plotAreaFrame release]; 200 [plots release]; 201 [plotSpaces release]; 202 [title release]; 203 [titleTextStyle release]; 204 [titleAnnotation release]; 205 [legend release]; 206 [legendAnnotation release]; 207 208 [super dealloc]; 209} 210 211#pragma mark - 212#pragma mark NSCoding methods 213 214-(void)encodeWithCoder:(NSCoder *)coder 215{ 216 [super encodeWithCoder:coder]; 217 218 [coder encodeObject:self.plotAreaFrame forKey:@"CPTGraph.plotAreaFrame"]; 219 [coder encodeObject:self.plots forKey:@"CPTGraph.plots"]; 220 [coder encodeObject:self.plotSpaces forKey:@"CPTGraph.plotSpaces"]; 221 [coder encodeObject:self.title forKey:@"CPTGraph.title"]; 222 [coder encodeObject:self.titleTextStyle forKey:@"CPTGraph.titleTextStyle"]; 223 [coder encodeInteger:self.titlePlotAreaFrameAnchor forKey:@"CPTGraph.titlePlotAreaFrameAnchor"]; 224 [coder encodeCPTPoint:self.titleDisplacement forKey:@"CPTGraph.titleDisplacement"]; 225 [coder encodeObject:self.titleAnnotation forKey:@"CPTGraph.titleAnnotation"]; 226 [coder encodeObject:self.legend forKey:@"CPTGraph.legend"]; 227 [coder encodeObject:self.legendAnnotation forKey:@"CPTGraph.legendAnnotation"]; 228 [coder encodeInteger:self.legendAnchor forKey:@"CPTGraph.legendAnchor"]; 229 [coder encodeCPTPoint:self.legendDisplacement forKey:@"CPTGraph.legendDisplacement"]; 230} 231 232-(id)initWithCoder:(NSCoder *)coder 233{ 234 if ( (self = [super initWithCoder:coder]) ) { 235 plotAreaFrame = [[coder decodeObjectForKey:@"CPTGraph.plotAreaFrame"] retain]; 236 plots = [[coder decodeObjectForKey:@"CPTGraph.plots"] mutableCopy]; 237 plotSpaces = [[coder decodeObjectForKey:@"CPTGraph.plotSpaces"] mutableCopy]; 238 title = [[coder decodeObjectForKey:@"CPTGraph.title"] copy]; 239 titleTextStyle = [[coder decodeObjectForKey:@"CPTGraph.titleTextStyle"] copy]; 240 titlePlotAreaFrameAnchor = [coder decodeIntegerForKey:@"CPTGraph.titlePlotAreaFrameAnchor"]; 241 titleDisplacement = [coder decodeCPTPointForKey:@"CPTGraph.titleDisplacement"]; 242 titleAnnotation = [[coder decodeObjectForKey:@"CPTGraph.titleAnnotation"] retain]; 243 legend = [[coder decodeObjectForKey:@"CPTGraph.legend"] retain]; 244 legendAnnotation = [[coder decodeObjectForKey:@"CPTGraph.legendAnnotation"] retain]; 245 legendAnchor = [coder decodeIntegerForKey:@"CPTGraph.legendAnchor"]; 246 legendDisplacement = [coder decodeCPTPointForKey:@"CPTGraph.legendDisplacement"]; 247 } 248 return self; 249} 250 251#pragma mark - 252#pragma mark Drawing 253 254-(void)layoutAndRenderInContext:(CGContextRef)context 255{ 256 [self reloadDataIfNeeded]; 257 [self.axisSet.axes makeObjectsPerformSelector:@selector(relabel)]; 258 [super layoutAndRenderInContext:context]; 259} 260 261#pragma mark - 262#pragma mark Animation 263 264+(BOOL)needsDisplayForKey:(NSString *)aKey 265{ 266 static NSArray *keys = nil; 267 268 if ( !keys ) { 269 keys = [[NSArray alloc] initWithObjects: 270 @"titleDisplacement", 271 @"legendDisplacement", 272 nil]; 273 } 274 275 if ( [keys containsObject:aKey] ) { 276 return YES; 277 } 278 else { 279 return [super needsDisplayForKey:aKey]; 280 } 281} 282 283#pragma mark - 284#pragma mark Retrieving Plots 285 286/** @brief Makes all plots reload their data. 287 **/ 288-(void)reloadData 289{ 290 [self.plots makeObjectsPerformSelector:@selector(reloadData)]; 291} 292 293/** @brief Makes all plots reload their data if their data cache is out of date. 294 **/ 295-(void)reloadDataIfNeeded 296{ 297 [self.plots makeObjectsPerformSelector:@selector(reloadDataIfNeeded)]; 298} 299 300/** @brief All plots associated with the graph. 301 * @return An array of all plots associated with the graph. 302 **/ 303-(NSArray *)allPlots 304{ 305 return [NSArray arrayWithArray:self.plots]; 306} 307 308/** @brief Gets the plot at the given index in the plot array. 309 * @param index An index within the bounds of the plot array. 310 * @return The plot at the given index. 311 **/ 312-(CPTPlot *)plotAtIndex:(NSUInteger)index 313{ 314 return [self.plots objectAtIndex:index]; 315} 316 317/** @brief Gets the plot with the given identifier from the plot array. 318 * @param identifier A plot identifier. 319 * @return The plot with the given identifier or nil if it was not found. 320 **/ 321-(CPTPlot *)plotWithIdentifier:(id <NSCopying>)identifier 322{ 323 for (CPTPlot *plot in self.plots) { 324 if ( [[plot identifier] isEqual:identifier] ) return plot; 325 } 326 return nil; 327} 328 329#pragma mark - 330#pragma mark Organizing Plots 331 332/** @brief Add a plot to the default plot space. 333 * @param plot The plot. 334 **/ 335-(void)addPlot:(CPTPlot *)plot 336{ 337 [self addPlot:plot toPlotSpace:self.defaultPlotSpace]; 338} 339 340/** @brief Add a plot to the given plot space. 341 * @param plot The plot. 342 * @param space The plot space. 343 **/ 344-(void)addPlot:(CPTPlot *)plot toPlotSpace:(CPTPlotSpace *)space 345{ 346 if ( plot ) { 347 [self.plots addObject:plot]; 348 plot.plotSpace = space; 349 plot.graph = self; 350 [self.plotAreaFrame.plotGroup addPlot:plot]; 351 } 352} 353 354/** @brief Remove a plot from the graph. 355 * @param plot The plot to remove. 356 **/ 357-(void)removePlot:(CPTPlot *)plot 358{ 359 if ( [self.plots containsObject:plot] ) { 360 plot.plotSpace = nil; 361 plot.graph = nil; 362 [self.plotAreaFrame.plotGroup removePlot:plot]; 363 [self.plots removeObject:plot]; 364 } 365 else { 366 [NSException raise:CPTException format:@"Tried to remove CPTPlot which did not exist."]; 367 } 368} 369 370/** @brief Add a plot to the default plot space at the given index in the plot array. 371 * @param plot The plot. 372 * @param index An index within the bounds of the plot array. 373 **/ 374-(void)insertPlot:(CPTPlot* )plot atIndex:(NSUInteger)index 375{ 376 [self insertPlot:plot atIndex:index intoPlotSpace:self.defaultPlotSpace]; 377} 378 379/** @brief Add a plot to the given plot space at the given index in the plot array. 380 * @param plot The plot. 381 * @param index An index within the bounds of the plot array. 382 * @param space The plot space. 383 **/ 384-(void)insertPlot:(CPTPlot* )plot atIndex:(NSUInteger)index intoPlotSpace:(CPTPlotSpace *)space 385{ 386 if (plot) { 387 [self.plots insertObject:plot atIndex:index]; 388 plot.plotSpace = space; 389 plot.graph = self; 390 [self.plotAreaFrame.plotGroup addPlot:plot]; 391 } 392} 393 394/** @brief Remove a plot from the graph. 395 * @param identifier The identifier of the plot to remove. 396 **/ 397-(void)removePlotWithIdentifier:(id <NSCopying>)identifier 398{ 399 CPTPlot* plotToRemove = [self plotWithIdentifier:identifier]; 400 if (plotToRemove) { 401 plotToRemove.plotSpace = nil; 402 plotToRemove.graph = nil; 403 [self.plotAreaFrame.plotGroup removePlot:plotToRemove]; 404 [self.plots removeObjectIdenticalTo:plotToRemove]; 405 } 406} 407 408#pragma mark - 409#pragma mark Retrieving Plot Spaces 410 411-(CPTPlotSpace *)defaultPlotSpace { 412 return ( self.plotSpaces.count > 0 ? [self.plotSpaces objectAtIndex:0] : nil ); 413} 414 415/** @brief All plot spaces associated with the graph. 416 * @return An array of all plot spaces associated with the graph. 417 **/ 418-(NSArray *)allPlotSpaces 419{ 420 return [NSArray arrayWithArray:self.plotSpaces]; 421} 422 423/** @brief Gets the plot space at the given index in the plot space array. 424 * @param index An index within the bounds of the plot space array. 425 * @return The plot space at the given index. 426 **/ 427-(CPTPlotSpace *)plotSpaceAtIndex:(NSUInteger)index 428{ 429 return ( self.plotSpaces.count > index ? [self.plotSpaces objectAtIndex:index] : nil ); 430} 431 432/** @brief Gets the plot space with the given identifier from the plot space array. 433 * @param identifier A plot space identifier. 434 * @return The plot space with the given identifier or nil if it was not found. 435 **/ 436-(CPTPlotSpace *)plotSpaceWithIdentifier:(id <NSCopying>)identifier 437{ 438 for (CPTPlotSpace *plotSpace in self.plotSpaces) { 439 if ( [[plotSpace identifier] isEqual:identifier] ) return plotSpace; 440 } 441 return nil; 442} 443 444#pragma mark - 445#pragma mark Set Plot Area 446 447-(void)setPlotAreaFrame:(CPTPlotAreaFrame *)newArea 448{ 449 if ( plotAreaFrame != newArea ) { 450 plotAreaFrame.graph = nil; 451 [plotAreaFrame removeFromSuperlayer]; 452 [plotAreaFrame release]; 453 plotAreaFrame = [newArea retain]; 454 [self addSublayer:newArea]; 455 plotAreaFrame.graph = self; 456 for ( CPTPlotSpace *space in self.plotSpaces ) { 457 space.graph = self; 458 } 459 } 460} 461 462#pragma mark - 463#pragma mark Organizing Plot Spaces 464 465/** @brief Add a plot space to the graph. 466 * @param space The plot space. 467 **/ 468-(void)addPlotSpace:(CPTPlotSpace *)space 469{ 470 [self.plotSpaces addObject:space]; 471 space.graph = self; 472 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(plotSpaceMappingDidChange:) name:CPTPlotSpaceCoordinateMappingDidChangeNotification object:space]; 473} 474 475/** @brief Remove a plot space from the graph. 476 * @param plotSpace The plot space. 477 **/ 478-(void)removePlotSpace:(CPTPlotSpace *)plotSpace 479{ 480 if ( [self.plotSpaces containsObject:plotSpace] ) { 481 [[NSNotificationCenter defaultCenter] removeObserver:self name:CPTPlotSpaceCoordinateMappingDidChangeNotification object:plotSpace]; 482 483 // Remove space 484 plotSpace.graph = nil; 485 [self.plotSpaces removeObject:plotSpace]; 486 487 // Update axes that referenced space 488 for ( CPTAxis *axis in self.axisSet.axes ) { 489 if ( axis.plotSpace == plotSpace ) axis.plotSpace = nil; 490 } 491 } 492 else { 493 [NSException raise:CPTException format:@"Tried to remove CPTPlotSpace which did not exist."]; 494 } 495} 496 497#pragma mark - 498#pragma mark Coordinate Changes in Plot Spaces 499 500-(void)plotSpaceMappingDidChange:(NSNotification *)notif 501{ 502 CPTPlotSpace *plotSpace = notif.object; 503 BOOL backgroundBandsNeedRedraw = NO; 504 505 for ( CPTAxis *axis in self.axisSet.axes ) { 506 if ( axis.plotSpace == plotSpace ) { 507 [axis setNeedsRelabel]; 508 backgroundBandsNeedRedraw |= (axis.backgroundLimitBands.count > 0); 509 } 510 } 511 for ( CPTPlot *plot in self.plots ) { 512 if ( plot.plotSpace == plotSpace ) { 513 [plot setNeedsDisplay]; 514 } 515 } 516 if ( backgroundBandsNeedRedraw ) { 517 [self.plotAreaFrame.plotArea setNeedsDisplay]; 518 } 519} 520 521#pragma mark - 522#pragma mark Axis Set 523 524-(CPTAxisSet *)axisSet 525{ 526 return self.plotAreaFrame.axisSet; 527} 528 529-(void)setAxisSet:(CPTAxisSet *)newSet 530{ 531 self.plotAreaFrame.axisSet = newSet; 532} 533 534#pragma mark - 535#pragma mark Themes 536 537/** @brief Apply a theme to style the graph. 538 * @param theme The theme object used to style the graph. 539 **/ 540-(void)applyTheme:(CPTTheme *)theme 541{ 542 [theme applyThemeToGraph:self]; 543} 544 545#pragma mark - 546#pragma mark Legend 547 548-(CPTLegend *)legend 549{ 550 return (CPTLegend *)self.legendAnnotation.contentLayer; 551} 552 553-(void)setLegend:(CPTLegend *)newLegend 554{ 555 if ( newLegend != legend ) { 556 [legend release]; 557 legend = [newLegend retain]; 558 CPTLayerAnnotation *theLegendAnnotation = self.legendAnnotation; 559 if ( legend ) { 560 if ( theLegendAnnotation ) { 561 theLegendAnnotation.contentLayer = legend; 562 } 563 else { 564 CPTLayerAnnotation *newLegendAnnotation = [[CPTLayerAnnotation alloc] initWithAnchorLayer:self]; 565 newLegendAnnotation.contentLayer = legend; 566 newLegendAnnotation.displacement = self.legendDisplacement; 567 newLegendAnnotation.rectAnchor = self.legendAnchor; 568 newLegendAnnotation.contentAnchorPoint = [self contentAnchorForLegend]; 569 [self addAnnotation:newLegendAnnotation]; 570 self.legendAnnotation = newLegendAnnotation; 571 [newLegendAnnotation release]; 572 } 573 } 574 else { 575 if ( theLegendAnnotation ) { 576 [self removeAnnotation:theLegendAnnotation]; 577 self.legendAnnotation = nil; 578 } 579 } 580 } 581} 582 583-(void)setLegendAnchor:(CPTRectAnchor)newLegendAnchor 584{ 585 if ( newLegendAnchor != legendAnchor ) { 586 legendAnchor = newLegendAnchor; 587 CPTLayerAnnotation *theLegendAnnotation = self.legendAnnotation; 588 if ( theLegendAnnotation ) { 589 theLegendAnnotation.rectAnchor = newLegendAnchor; 590 theLegendAnnotation.contentAnchorPoint = [self contentAnchorForLegend]; 591 } 592 } 593} 594 595-(void)setLegendDisplacement:(CGPoint)newLegendDisplacement 596{ 597 if ( !CGPointEqualToPoint(newLegendDisplacement, legendDisplacement) ) { 598 legendDisplacement = newLegendDisplacement; 599 self.legendAnnotation.displacement = newLegendDisplacement; 600 } 601} 602 603-(CGPoint)contentAnchorForLegend 604{ 605 CGPoint contentAnchor = CGPointZero; 606 607 switch ( self.legendAnchor ) { 608 case CPTRectAnchorBottomLeft: 609 contentAnchor = CGPointMake(0.0, 0.0); 610 break; 611 case CPTRectAnchorBottom: 612 contentAnchor = CGPointMake(0.5, 0.0); 613 break; 614 case CPTRectAnchorBottomRight: 615 contentAnchor = CGPointMake(1.0, 0.0); 616 break; 617 case CPTRectAnchorLeft: 618 contentAnchor = CGPointMake(0.0, 0.5); 619 break; 620 case CPTRectAnchorRight: 621 contentAnchor = CGPointMake(1.0, 0.5); 622 break; 623 case CPTRectAnchorTopLeft: 624 contentAnchor = CGPointMake(0.0, 1.0); 625 break; 626 case CPTRectAnchorTop: 627 contentAnchor = CGPointMake(0.5, 1.0); 628 break; 629 case CPTRectAnchorTopRight: 630 contentAnchor = CGPointMake(1.0, 1.0); 631 break; 632 case CPTRectAnchorCenter: 633 contentAnchor = CGPointMake(0.5, 0.5); 634 break; 635 default: 636 break; 637 } 638 639 return contentAnchor; 640} 641 642#pragma mark - 643#pragma mark Accessors 644 645-(void)setPaddingLeft:(CGFloat)newPadding 646{ 647 if ( newPadding != self.paddingLeft ) { 648 [super setPaddingLeft:newPadding]; 649 [self.axisSet.axes makeObjectsPerformSelector:@selector(setNeedsDisplay)]; 650 } 651} 652 653-(void)setPaddingRight:(CGFloat)newPadding 654{ 655 if ( newPadding != self.paddingRight ) { 656 [super setPaddingRight:newPadding]; 657 [self.axisSet.axes makeObjectsPerformSelector:@selector(setNeedsDisplay)]; 658 } 659} 660 661-(void)setPaddingTop:(CGFloat)newPadding 662{ 663 if ( newPadding != self.paddingTop ) { 664 [super setPaddingTop:newPadding]; 665 [self.axisSet.axes makeObjectsPerformSelector:@selector(setNeedsDisplay)]; 666 } 667} 668 669-(void)setPaddingBottom:(CGFloat)newPadding 670{ 671 if ( newPadding != self.paddingBottom ) { 672 [super setPaddingBottom:newPadding]; 673 [self.axisSet.axes makeObjectsPerformSelector:@selector(setNeedsDisplay)]; 674 } 675} 676 677-(NSArray *)topDownLayerOrder 678{ 679 return self.plotAreaFrame.plotArea.topDownLayerOrder; 680} 681 682-(void)setTopDownLayerOrder:(NSArray *)newArray 683{ 684 self.plotAreaFrame.plotArea.topDownLayerOrder = newArray; 685} 686 687-(void)setTitle:(NSString *)newTitle 688{ 689 if ( newTitle != title ) { 690 [title release]; 691 title = [newTitle copy]; 692 CPTLayerAnnotation *theTitleAnnotation = self.titleAnnotation; 693 if ( title ) { 694 if ( theTitleAnnotation ) { 695 ((CPTTextLayer *)theTitleAnnotation.contentLayer).text = title; 696 } 697 else { 698 CPTLayerAnnotation *newTitleAnnotation = [[CPTLayerAnnotation alloc] initWithAnchorLayer:self.plotAreaFrame]; 699 CPTTextLayer *newTextLayer = [[CPTTextLayer alloc] initWithText:title style:self.titleTextStyle]; 700 newTitleAnnotation.contentLayer = newTextLayer; 701 newTitleAnnotation.displacement = self.titleDisplacement; 702 newTitleAnnotation.rectAnchor = self.titlePlotAreaFrameAnchor; 703 [self addAnnotation:newTitleAnnotation]; 704 self.titleAnnotation = newTitleAnnotation; 705 [newTextLayer release]; 706 [newTitleAnnotation release]; 707 } 708 } 709 else { 710 if ( theTitleAnnotation ) { 711 [self removeAnnotation:theTitleAnnotation]; 712 self.titleAnnotation = nil; 713 } 714 } 715 } 716} 717 718-(void)setTitleTextStyle:(CPTMutableTextStyle *)newStyle 719{ 720 if ( newStyle != titleTextStyle ) { 721 [titleTextStyle release]; 722 titleTextStyle = [newStyle copy]; 723 ((CPTTextLayer *)self.titleAnnotation.contentLayer).textStyle = titleTextStyle; 724 } 725} 726 727-(void)setTitleDisplacement:(CGPoint)newDisplace 728{ 729 if ( !CGPointEqualToPoint(newDisplace, titleDisplacement) ) { 730 titleDisplacement = newDisplace; 731 titleAnnotation.displacement = newDisplace; 732 } 733} 734 735-(void)setTitlePlotAreaFrameAnchor:(CPTRectAnchor)newAnchor 736{ 737 if ( newAnchor != titlePlotAreaFrameAnchor ) { 738 titlePlotAreaFrameAnchor = newAnchor; 739 titleAnnotation.rectAnchor = titlePlotAreaFrameAnchor; 740 } 741} 742 743#pragma mark - 744#pragma mark Event Handling 745 746-(BOOL)pointingDeviceDownEvent:(id)event atPoint:(CGPoint)interactionPoint 747{ 748 // Plots 749 for ( CPTPlot *plot in [self.plots reverseObjectEnumerator] ) { 750 if ( [plot pointingDeviceDownEvent:event atPoint:interactionPoint] ) return YES; 751 } 752 753 // Axes Set 754 if ( [self.axisSet pointingDeviceDownEvent:event atPoint:interactionPoint] ) return YES; 755 756 // Plot area 757 if ( [self.plotAreaFrame pointingDeviceDownEvent:event atPoint:interactionPoint] ) return YES; 758 759 // Plot spaces 760 // Plot spaces do not block events, because several spaces may need to receive 761 // the same event sequence (e.g., dragging coordinate translation) 762 BOOL handledEvent = NO; 763 for ( CPTPlotSpace *space in self.plotSpaces ) { 764 BOOL handled = [space pointingDeviceDownEvent:event atPoint:interactionPoint]; 765 handledEvent |= handled; 766 } 767 768 return handledEvent; 769} 770 771-(BOOL)pointingDeviceUpEvent:(id)event atPoint:(CGPoint)interactionPoint 772{ 773 // Plots 774 for ( CPTPlot *plot in [self.plots reverseObjectEnumerator] ) { 775 if ( [plot pointingDeviceUpEvent:event atPoint:interactionPoint] ) return YES; 776 } 777 778 // Axes Set 779 if ( [self.axisSet pointingDeviceUpEvent:event atPoint:interactionPoint] ) return YES; 780 781 // Plot area 782 if ( [self.plotAreaFrame pointingDeviceUpEvent:event atPoint:interactionPoint] ) return YES; 783 784 // Plot spaces 785 // Plot spaces do not block events, because several spaces may need to receive 786 // the same event sequence (e.g., dragging coordinate translation) 787 BOOL handledEvent = NO; 788 for ( CPTPlotSpace *space in self.plotSpaces ) { 789 BOOL handled = [space pointingDeviceUpEvent:event atPoint:interactionPoint]; 790 handledEvent |= handled; 791 } 792 793 return handledEvent; 794} 795 796-(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint 797{ 798 // Plots 799 for ( CPTPlot *plot in [self.plots reverseObjectEnumerator] ) { 800 if ( [plot pointingDeviceDraggedEvent:event atPoint:interactionPoint] ) return YES; 801 } 802 803 // Axes Set 804 if ( [self.axisSet pointingDeviceDraggedEvent:event atPoint:interactionPoint] ) return YES; 805 806 // Plot area 807 if ( [self.plotAreaFrame pointingDeviceDraggedEvent:event atPoint:interactionPoint] ) return YES; 808 809 // Plot spaces 810 // Plot spaces do not block events, because several spaces may need to receive 811 // the same event sequence (e.g., dragging coordinate translation) 812 BOOL handledEvent = NO; 813 for ( CPTPlotSpace *space in self.plotSpaces ) { 814 BOOL handled = [space pointingDeviceDraggedEvent:event atPoint:interactionPoint]; 815 handledEvent |= handled; 816 } 817 818 return handledEvent; 819} 820 821-(BOOL)pointingDeviceCancelledEvent:(id)event 822{ 823 // Plots 824 for ( CPTPlot *plot in [self.plots reverseObjectEnumerator] ) { 825 if ( [plot pointingDeviceCancelledEvent:event] ) return YES; 826 } 827 828 // Axes Set 829 if ( [self.axisSet pointingDeviceCancelledEvent:event] ) return YES; 830 831 // Plot area 832 if ( [self.plotAreaFrame pointingDeviceCancelledEvent:event] ) return YES; 833 834 // Plot spaces 835 BOOL handledEvent = NO; 836 for ( CPTPlotSpace *space in self.plotSpaces ) { 837 BOOL handled = [space pointingDeviceCancelledEvent:event]; 838 handledEvent |= handled; 839 } 840 841 return handledEvent; 842} 843 844@end 845 846#pragma mark - 847 848@implementation CPTGraph(AbstractFactoryMethods) 849 850/** @brief Creates a new plot space for the graph. 851 * @return A new plot space. 852 **/ 853-(CPTPlotSpace *)newPlotSpace 854{ 855 return nil; 856} 857 858/** @brief Creates a new axis set for the graph. 859 * @return A new axis set. 860 **/ 861-(CPTAxisSet *)newAxisSet 862{ 863 return nil; 864} 865 866@end 867