// // MPExpressionLayout.m // MathPad // // Created by Kim Wittenburg on 07.08.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPExpressionLayout.h" #import "MPFunctionLayout.h" @interface MPExpressionLayout (MPLineGeneration) - (CTLineRef)lineForElementAtIndex:(NSUInteger)index; - (CTLineRef)createLineForString:(NSString *)aString; @end @implementation MPExpressionLayout (MPLineGeneration) - (CTLineRef)lineForElementAtIndex:(NSUInteger)index { id element = [self.expression elementAtIndex:index]; if (![element isString]) { return NULL; } NSString *string = element; id lineObject = [self cachableObjectForIndex:index generator:^id{ CTLineRef line = [self createLineForString:string]; return CFBridgingRelease(line); }]; return (__bridge CTLineRef)lineObject; } - (CTLineRef)createLineForString:(NSString *)aString { NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}]; CFAttributedStringRef attributedString = CFBridgingRetain(text); CTLineRef line = CTLineCreateWithAttributedString(attributedString); CFRelease(attributedString); // TODO: Is this release appropriate? return line; } @end @implementation MPExpressionLayout # pragma mark Creation Methods - (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage { self = [super init]; if (self) { _expressionStorage = expressionStorage; _expression = expressionStorage; } return self; } - (instancetype)initWithPath:(NSIndexPath *)path parent:(MPLayout *)parent { self = [super initWithPath:path parent:parent]; if (self) { _expression = [parent.expressionStorage elementAtIndexPath:path]; } return self; } #pragma mark Properties @synthesize expressionStorage = _expressionStorage; - (MPExpressionStorage *)expressionStorage { if (_expressionStorage) { return _expressionStorage; } return self.parent.expressionStorage; } //- (MPExpression *)expression //{ // return [self.expressionStorage elementAtIndexPath:self.path]; //} #pragma mark Cache Methods - (MPLayout *)childLayoutAtIndex:(NSUInteger)index { id cachedObject = [self cachableObjectForIndex:index generator:^id{ NSIndexPath *indexPath = [self.expression.indexPath indexPathByAddingIndex:index]; MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath parent:self]; return layout; }]; if ([cachedObject isKindOfClass:[MPLayout class]]) { return cachedObject; } return nil; } - (NSSize)sizeForElementAtIndex:(NSUInteger)index { id symbol = [self.expression elementAtIndex:index]; if ([symbol isString]) { CTLineRef line = [self lineForElementAtIndex:index]; CFRetain(line); CGRect bounds = CTLineGetBoundsWithOptions(line, /*kCTLineBoundsUseOpticalBounds*/ 0); CFRelease(line); return bounds.size; } else { return [self childLayoutAtIndex:index].size; } } #pragma mark Drawing Methods - (NSSize)generateSize { CGFloat width = 0, height = 0; for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) { NSSize elementSize = [self sizeForElementAtIndex:index]; width += elementSize.width; height = MAX(height, elementSize.height); } return NSMakeSize(width, height); } - (void)drawAtPoint:(NSPoint)point { // Get the current context CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; CGContextSaveGState(context); // Set the text matrix CGContextSetTextMatrix(context, CGAffineTransformIdentity); // Track the x position CGFloat x = point.x; for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) { // The current element id element = [self.expression elementAtIndex:index]; NSSize elementSize = [self sizeForElementAtIndex:index]; CGFloat dy = (self.size.height - elementSize.height) / 2; CGFloat y = point.y + dy; if ([element isString]) { // Get the line to draw CTLineRef line = [self lineForElementAtIndex:index]; CFRetain(line); // Move to the appropriate position CGContextSetTextPosition(context, x, y); // Perform the drawing CTLineDraw(line, context); CFRelease(line); } else { // Let the child layout draw itself MPLayout *layout = [self childLayoutAtIndex:index]; [layout drawAtPoint:NSMakePoint(x, y)]; } x += elementSize.width; } CGContextRestoreGState(context); } @end