// // BlastView.m // BioAlignmentStudio // // Created by Alexander K. Hudek on 2008-09-22.. // #import "BlastView.h" @implementation BlastView - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { // this will hold the set of layout managers, one for each sequence textStores = [[NSMutableArray alloc] init]; // Select fixed width font myFont = [NSFont fontWithName:@"AndaleMono" size:11]; // Setup formatting and drawing for coordinates. NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]]; [paragraphStyle setAlignment:NSRightTextAlignment]; textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: myFont, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil]; coordinateFormatter = [[NSNumberFormatter alloc] init]; [coordinateFormatter setLocale:[NSLocale currentLocale]]; [coordinateFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; // Store size of font character. NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; NSTextContainer *textContainer = [[NSTextContainer alloc] init]; NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:@"W"]; [layoutManager addTextContainer:textContainer]; [textStorage addLayoutManager:layoutManager]; [textContainer setLineFragmentPadding:0.0]; [textStorage addAttribute:NSFontAttributeName value:myFont range:NSMakeRange(0, [textStorage length])]; NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; characterSize = boundingRect.size; // Store margines. Set top, bottom, and bottom to two characters. bottomMargin = topMargin = characterSize.height*2; // Right margin is four characters. rightMargin = characterSize.width*4; // Left margin is 20 characters. leftMargin = characterSize.width*15; charactersPerLine = 60; } return self; } - (void)setAlignmentData:(BCSequenceArray *)alignment { // clear the existing data [textStores removeAllObjects]; // for each sequence, add new layout manager NSEnumerator *iter = [alignment sequenceEnumerator]; BCSequence *aSequence; while( aSequence = [iter nextObject] ) { NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; NSTextContainer *textContainer = [[NSTextContainer alloc] init]; NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:[aSequence sequenceString]]; [layoutManager addTextContainer:textContainer]; [textStorage addLayoutManager:layoutManager]; [textStores addObject:textStorage]; [textContainer setLineFragmentPadding:0.0]; [textStorage addAttribute:NSFontAttributeName value:myFont range:NSMakeRange(0, [textStorage length])]; // Compute dimensions of document. blockSize.height = characterSize.height*[textStores count]; blockSize.width = charactersPerLine*characterSize.width; numBlocks = ceil( (CGFloat)[[textStores objectAtIndex:0] length]/(CGFloat)charactersPerLine ); // Width is set by the number of symbols plus margins. NSRect new_frame = [self frame]; new_frame.size.width = leftMargin + rightMargin + blockSize.width; new_frame.size.height = topMargin + bottomMargin; // each block has an extra line to divide them, except the last block new_frame.size.height += (blockSize.height+characterSize.height)*numBlocks - characterSize.height; [self setFrame:new_frame]; } } - (BOOL)isOpaque { return TRUE; } - (int)nextBlockAtHeight:(CGFloat)height { // nothing to do since on bottom margin if( height <= bottomMargin ) return -1; NSRect myframe = [self frame]; if( height >= (myframe.size.height - topMargin) ) return 0; // otherwise determine starting block return numBlocks - 1 - floor( (height-bottomMargin)/(blockSize.height+characterSize.height) ); } - (void)drawRect:(NSRect)rect { // set background [[NSColor whiteColor] set]; [NSBezierPath fillRect:rect]; //NSLog( @"%f %f %f %f %f", rect.origin.y, rect.size.height, bottomMargin, blockSize.height, characterSize.height ); // determine starting block to draw int start_block = [self nextBlockAtHeight:(rect.origin.y + rect.size.height)]; if( start_block == -1 ) return; // Draw each sequence. for( int ts_i = 0; ts_i < [textStores count]; ++ts_i ) { NSTextStorage *textStorage = [textStores objectAtIndex:ts_i]; NSLayoutManager *layoutManager = [[textStorage layoutManagers] lastObject]; NSPoint start; start.x = rect.origin.x + leftMargin; start.y = bottomMargin + (CGFloat)(numBlocks - start_block)*(blockSize.height + characterSize.height) - characterSize.height*(ts_i+2); NSRange characterRange = NSMakeRange(start_block*charactersPerLine, charactersPerLine); //NSLog( @"%f %d %d %f", start.y, numBlocks, start_block, myframe.size.height ); while( start.y > (bottomMargin - characterSize.height) && start.y > (rect.origin.y - characterSize.height) ) { //NSLog( @"%d %f", ts_i, start.y ); if( [textStorage length] < NSMaxRange(characterRange) ) characterRange.length = [textStorage length] - characterRange.location; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:characterRange actualCharacterRange:nil ]; NSRect lineFragmentRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:NULL]; NSPoint layoutLocation = [layoutManager locationForGlyphAtIndex:glyphRange.location]; layoutLocation.x += lineFragmentRect.origin.x; layoutLocation.y += lineFragmentRect.origin.y; // Draw line of text. [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:NSMakePoint(-layoutLocation.x+start.x,-layoutLocation.y+start.y)]; // Draw coordinate. NSNumber *adjPos = [NSNumber numberWithInt:characterRange.location+1]; NSString *posString = [coordinateFormatter stringFromNumber:adjPos]; NSRect posRect; posRect.origin.y = start.y; posRect.origin.x = 0; posRect.size.height = characterSize.height; posRect.size.width = leftMargin - characterSize.width; [posString drawInRect:posRect withAttributes:textAttributes]; // Move start position down. start.y -= blockSize.height+characterSize.height; // Move character counter. characterRange.location += charactersPerLine; } } } @end