git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

HexFiend/HFLineCountingView.m

      1 //
      2 //  HFLineCountingView.m
      3 //  HexFiend_2
      4 //
      5 //  Copyright 2007 ridiculous_fish. All rights reserved.
      6 //
      7 
      8 #import <HexFiend/HFLineCountingView.h>
      9 #import <HexFiend/HFLineCountingRepresenter.h>
     10 #import <HexFiend/HFFunctions.h>
     11 
     12 #define TIME_LINE_NUMBERS 0
     13 
     14 #define HEX_LINE_NUMBERS_HAVE_0X_PREFIX 0
     15 
     16 #define INVALID_LINE_COUNT NSUIntegerMax
     17 
     18 #if TIME_LINE_NUMBERS
     19 @interface HFTimingTextView : NSTextView
     20 @end
     21 @implementation HFTimingTextView
     22 - (void)drawRect:(NSRect)rect {
     23     CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
     24     [super drawRect:rect];
     25     CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
     26     NSLog(@"TextView line number time: %f", endTime - startTime);
     27 }
     28 @end
     29 #endif
     30 
     31 @implementation HFLineCountingView
     32 
     33 - (void)_sharedInitLineCountingView {
     34     layoutManager = [[NSLayoutManager alloc] init];
     35     textStorage = [[NSTextStorage alloc] init];
     36     [textStorage addLayoutManager:layoutManager];
     37     textContainer = [[NSTextContainer alloc] init];
     38     [textContainer setLineFragmentPadding:(CGFloat)5];
     39     [textContainer setContainerSize:NSMakeSize(self.bounds.size.width, [textContainer containerSize].height)];
     40     [layoutManager addTextContainer:textContainer];
     41 }
     42 
     43 - (instancetype)initWithFrame:(NSRect)frame {
     44     self = [super initWithFrame:frame];
     45     if (self) {
     46         [self _sharedInitLineCountingView];
     47     }
     48     return self;
     49 }
     50 
     51 - (void)dealloc {
     52     HFUnregisterViewForWindowAppearanceChanges(self, registeredForAppNotifications);
     53     [_font release];
     54     [layoutManager release];
     55     [textContainer release];
     56     [textStorage release];
     57     [textAttributes release];
     58     [super dealloc];
     59 }
     60 
     61 - (void)encodeWithCoder:(NSCoder *)coder {
     62     HFASSERT([coder allowsKeyedCoding]);
     63     [super encodeWithCoder:coder];
     64     [coder encodeObject:_font forKey:@"HFFont"];
     65     [coder encodeDouble:_lineHeight forKey:@"HFLineHeight"];
     66     [coder encodeObject:_representer forKey:@"HFRepresenter"];
     67     [coder encodeInt64:_bytesPerLine forKey:@"HFBytesPerLine"];
     68     [coder encodeInt64:_lineNumberFormat forKey:@"HFLineNumberFormat"];
     69     [coder encodeBool:useStringDrawingPath forKey:@"HFUseStringDrawingPath"];
     70 }
     71 
     72 - (instancetype)initWithCoder:(NSCoder *)coder {
     73     HFASSERT([coder allowsKeyedCoding]);
     74     self = [super initWithCoder:coder];
     75     [self _sharedInitLineCountingView];
     76     _font = [[coder decodeObjectForKey:@"HFFont"] retain];
     77     _lineHeight = (CGFloat)[coder decodeDoubleForKey:@"HFLineHeight"];
     78     _representer = [coder decodeObjectForKey:@"HFRepresenter"];
     79     _bytesPerLine = (NSUInteger)[coder decodeInt64ForKey:@"HFBytesPerLine"];
     80     _lineNumberFormat = (NSUInteger)[coder decodeInt64ForKey:@"HFLineNumberFormat"];
     81     useStringDrawingPath = [coder decodeBoolForKey:@"HFUseStringDrawingPath"];
     82     return self;
     83 }
     84 
     85 - (BOOL)isFlipped { return YES; }
     86 
     87 - (void)getLineNumberFormatString:(char *)outString length:(NSUInteger)length {
     88     HFLineNumberFormat format = self.lineNumberFormat;
     89     if (format == HFLineNumberFormatDecimal) {
     90         strlcpy(outString, "%llu", length);
     91     }
     92     else if (format == HFLineNumberFormatHexadecimal) {
     93 #if HEX_LINE_NUMBERS_HAVE_0X_PREFIX
     94         // we want a format string like 0x%08llX
     95         snprintf(outString, length, "0x%%0%lullX", (unsigned long)self.representer.digitCount - 2);
     96 #else
     97         // we want a format string like %08llX
     98         snprintf(outString, length, "%%0%lullX", (unsigned long)self.representer.digitCount);
     99 #endif
    100     }
    101     else {
    102         strlcpy(outString, "", length);
    103     }
    104 }
    105 
    106 - (void)windowDidChangeKeyStatus:(NSNotification *)note {
    107     USE(note);
    108     [self setNeedsDisplay:YES];
    109 }
    110 
    111 - (void)viewDidMoveToWindow {
    112     HFRegisterViewForWindowAppearanceChanges(self, @selector(windowDidChangeKeyStatus:), !registeredForAppNotifications);
    113     registeredForAppNotifications = YES;
    114     [super viewDidMoveToWindow];
    115 }
    116 
    117 - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
    118     HFUnregisterViewForWindowAppearanceChanges(self, NO);
    119     [super viewWillMoveToWindow:newWindow];
    120 }
    121 
    122 - (void)drawDividerWithClip:(NSRect)clipRect {
    123     USE(clipRect);
    124     
    125 
    126 #if 1    
    127     NSInteger edges = _representer.borderedEdges;
    128     NSRect bounds = self.bounds;
    129     
    130     
    131     // -1 means to draw no edges
    132     if (edges == -1) {
    133         edges = 0;
    134     }
    135     
    136     [_representer.borderColor set];
    137     
    138     if ((edges & (1 << NSMinXEdge)) > 0) {
    139         NSRect lineRect = bounds;
    140         lineRect.size.width = 1;
    141         lineRect.origin.x = 0;
    142         if (NSIntersectsRect(lineRect, clipRect)) {
    143             NSRectFill(lineRect);
    144         }
    145     }
    146     
    147     if ((edges & (1 << NSMaxXEdge)) > 0) {
    148         NSRect lineRect = bounds;
    149         lineRect.size.width = 1;
    150         lineRect.origin.x = NSMaxX(bounds) - lineRect.size.width;
    151         if (NSIntersectsRect(lineRect, clipRect)) {
    152             NSRectFill(lineRect);
    153         }
    154     }
    155     
    156     if ((edges & (1 << NSMinYEdge)) > 0) {
    157         NSRect lineRect = bounds;
    158         lineRect.size.height = 1;
    159         lineRect.origin.y = 0;
    160         if (NSIntersectsRect(lineRect, clipRect)) {
    161             NSRectFill(lineRect);
    162         }
    163     }
    164     
    165     if ((edges & (1 << NSMaxYEdge)) > 0) {
    166         NSRect lineRect = bounds;
    167         lineRect.size.height = 1;
    168         lineRect.origin.y = NSMaxY(bounds) - lineRect.size.height;
    169         if (NSIntersectsRect(lineRect, clipRect)) {
    170             NSRectFill(lineRect);
    171         }
    172     }
    173     
    174     
    175     // Backwards compatibility to always draw a border on the edge with the interior shadow
    176     
    177     NSRect lineRect = bounds;
    178     lineRect.size.width = 1;
    179     NSInteger shadowEdge = _representer.interiorShadowEdge;
    180     if (shadowEdge == NSMaxXEdge) {
    181         lineRect.origin.x = NSMaxX(bounds) - lineRect.size.width;
    182     } else if (shadowEdge == NSMinXEdge) {
    183         lineRect.origin.x = NSMinX(bounds);
    184     } else {
    185         lineRect = NSZeroRect;
    186     }
    187     
    188     if (NSIntersectsRect(lineRect, clipRect)) {
    189         NSRectFill(lineRect);
    190     }
    191     
    192 #else
    193     
    194     
    195     if (NSIntersectsRect(lineRect, clipRect)) {
    196         // this looks better when we have no shadow
    197         [[NSColor lightGrayColor] set];
    198         NSRect bounds = self.bounds;
    199         NSRect lineRect = bounds;
    200         lineRect.origin.x += lineRect.size.width - 2;
    201         lineRect.size.width = 1;
    202         NSRectFill(NSIntersectionRect(lineRect, clipRect));
    203         [[NSColor whiteColor] set];
    204         lineRect.origin.x += 1;
    205         NSRectFill(NSIntersectionRect(lineRect, clipRect));	
    206     }
    207 #endif
    208 }
    209 
    210 static inline int common_prefix_length(const char *a, const char *b) {
    211     int i;
    212     for (i=0; ; i++) {
    213         char ac = a[i];
    214         char bc = b[i];
    215         if (ac != bc || ac == 0 || bc == 0) break;
    216     }
    217     return i;
    218 }
    219 
    220 /* Drawing with NSLayoutManager is necessary because the 10_2 typesetting behavior used by the old string drawing does the wrong thing for fonts like Bitstream Vera Sans Mono.  Also it's an optimization for drawing the shadow. */
    221 - (void)drawLineNumbersWithClipLayoutManagerPerLine:(NSRect)clipRect {
    222 #if TIME_LINE_NUMBERS
    223     CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    224 #endif
    225     NSUInteger previousTextStorageCharacterCount = [textStorage length];
    226     
    227     CGFloat verticalOffset = ld2f(_lineRangeToDraw.location - floorl(_lineRangeToDraw.location));
    228     NSRect textRect = self.bounds;
    229     textRect.size.height = _lineHeight;
    230     textRect.origin.y -= verticalOffset * _lineHeight;
    231     unsigned long long lineIndex = HFFPToUL(floorl(_lineRangeToDraw.location));
    232     unsigned long long lineValue = lineIndex * _bytesPerLine;
    233     NSUInteger linesRemaining = ll2l(HFFPToUL(ceill(_lineRangeToDraw.length + _lineRangeToDraw.location) - floorl(_lineRangeToDraw.location)));
    234     char previousBuff[256];
    235     int previousStringLength = (int)previousTextStorageCharacterCount;
    236     BOOL conversionResult = [[textStorage string] getCString:previousBuff maxLength:sizeof previousBuff encoding:NSASCIIStringEncoding];
    237     HFASSERT(conversionResult);
    238     while (linesRemaining--) {
    239         char formatString[64];
    240         [self getLineNumberFormatString:formatString length:sizeof formatString];
    241         
    242         if (NSIntersectsRect(textRect, clipRect)) {
    243             NSString *replacementCharacters = nil;
    244             NSRange replacementRange;
    245             char buff[256];
    246             int newStringLength = snprintf(buff, sizeof buff, formatString, lineValue);
    247             HFASSERT(newStringLength > 0);
    248             int prefixLength = common_prefix_length(previousBuff, buff);
    249             HFASSERT(prefixLength <= newStringLength);
    250             HFASSERT(prefixLength <= previousStringLength);
    251             replacementRange = NSMakeRange(prefixLength, previousStringLength - prefixLength);
    252             replacementCharacters = [[NSString alloc] initWithBytesNoCopy:buff + prefixLength length:newStringLength - prefixLength encoding:NSASCIIStringEncoding freeWhenDone:NO];
    253             NSUInteger glyphCount;
    254             [textStorage replaceCharactersInRange:replacementRange withString:replacementCharacters];
    255             if (previousTextStorageCharacterCount == 0) {
    256                 NSDictionary *atts = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, nil];
    257                 [textStorage setAttributes:atts range:NSMakeRange(0, newStringLength)];
    258                 [atts release];
    259             }
    260             glyphCount = [layoutManager numberOfGlyphs];
    261             if (glyphCount > 0) {
    262                 CGFloat maxX = NSMaxX([layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphCount - 1 effectiveRange:NULL]);
    263                 [layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, glyphCount) atPoint:NSMakePoint(textRect.origin.x + textRect.size.width - maxX, textRect.origin.y)];
    264             }
    265             previousTextStorageCharacterCount = newStringLength;
    266             [replacementCharacters release];
    267             memcpy(previousBuff, buff, newStringLength + 1);
    268             previousStringLength = newStringLength;
    269         }
    270         textRect.origin.y += _lineHeight;
    271         lineIndex++;
    272         lineValue = HFSum(lineValue, _bytesPerLine);
    273     }
    274 #if TIME_LINE_NUMBERS
    275     CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
    276     NSLog(@"Line number time: %f", endTime - startTime);
    277 #endif
    278 }
    279 
    280 - (void)drawLineNumbersWithClipStringDrawing:(NSRect)clipRect {
    281     CGFloat verticalOffset = ld2f(_lineRangeToDraw.location - floorl(_lineRangeToDraw.location));
    282     NSRect textRect = self.bounds;
    283     textRect.size.height = _lineHeight;
    284     textRect.size.width -= 5;
    285     textRect.origin.y -= verticalOffset * _lineHeight + 1;
    286     unsigned long long lineIndex = HFFPToUL(floorl(_lineRangeToDraw.location));
    287     unsigned long long lineValue = lineIndex * _bytesPerLine;
    288     NSUInteger linesRemaining = ll2l(HFFPToUL(ceill(_lineRangeToDraw.length + _lineRangeToDraw.location) - floorl(_lineRangeToDraw.location)));
    289     if (! textAttributes) {
    290         NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    291         [mutableStyle setAlignment:NSRightTextAlignment];
    292         NSParagraphStyle *paragraphStyle = [mutableStyle copy];
    293         [mutableStyle release];
    294         textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
    295         [paragraphStyle release];
    296     }
    297     
    298     char formatString[64];
    299     [self getLineNumberFormatString:formatString length:sizeof formatString];
    300     
    301     while (linesRemaining--) {
    302         if (NSIntersectsRect(textRect, clipRect)) {
    303             char buff[256];
    304             int newStringLength = snprintf(buff, sizeof buff, formatString, lineValue);
    305             HFASSERT(newStringLength > 0);
    306             NSString *string = [[NSString alloc] initWithBytesNoCopy:buff length:newStringLength encoding:NSASCIIStringEncoding freeWhenDone:NO];
    307             [string drawInRect:textRect withAttributes:textAttributes];
    308             [string release];
    309         }
    310         textRect.origin.y += _lineHeight;
    311         lineIndex++;
    312         if (linesRemaining > 0) lineValue = HFSum(lineValue, _bytesPerLine); //we could do this unconditionally, but then we risk overflow
    313     }
    314 }
    315 
    316 - (NSUInteger)characterCountForLineRange:(HFRange)range {
    317     HFASSERT(range.length <= NSUIntegerMax);
    318     NSUInteger characterCount;
    319     
    320     NSUInteger lineCount = ll2l(range.length);
    321     const NSUInteger stride = _bytesPerLine;
    322     HFLineCountingRepresenter *rep = self.representer;
    323     HFLineNumberFormat format = self.lineNumberFormat;
    324     if (format == HFLineNumberFormatDecimal) {
    325         unsigned long long lineValue = HFProductULL(range.location, _bytesPerLine);
    326         characterCount = lineCount /* newlines */;
    327         while (lineCount--) {
    328             characterCount += HFCountDigitsBase10(lineValue);
    329             lineValue += stride;
    330         }
    331     }
    332     else if (format == HFLineNumberFormatHexadecimal) {
    333         characterCount = ([rep digitCount] + 1) * lineCount; // +1 for newlines
    334     }
    335     else {
    336         characterCount = -1;
    337     }
    338     return characterCount;
    339 }
    340 
    341 - (NSString *)newLineStringForRange:(HFRange)range {
    342     HFASSERT(range.length <= NSUIntegerMax);
    343     if(range.length == 0)
    344         return [[NSString alloc] init]; // Placate the analyzer.
    345     
    346     NSUInteger lineCount = ll2l(range.length);
    347     const NSUInteger stride = _bytesPerLine;
    348     unsigned long long lineValue = HFProductULL(range.location, _bytesPerLine);
    349     NSUInteger characterCount = [self characterCountForLineRange:range];
    350     char *buffer = check_malloc(characterCount);
    351     NSUInteger bufferIndex = 0;
    352     
    353     char formatString[64];
    354     [self getLineNumberFormatString:formatString length:sizeof formatString];
    355     
    356     while (lineCount--) {
    357         int charCount = sprintf(buffer + bufferIndex, formatString, lineValue + self.representer.valueOffset);
    358         HFASSERT(charCount > 0);
    359         bufferIndex += charCount;
    360         buffer[bufferIndex++] = '\n';   
    361         lineValue += stride;
    362     }
    363     HFASSERT(bufferIndex == characterCount);
    364     
    365     NSString *string = [[NSString alloc] initWithBytesNoCopy:(void *)buffer length:bufferIndex encoding:NSASCIIStringEncoding freeWhenDone:YES];
    366     return string;
    367 }
    368 
    369 - (void)updateLayoutManagerWithLineIndex:(unsigned long long)startingLineIndex lineCount:(NSUInteger)linesRemaining {
    370     const BOOL debug = NO;
    371     [textStorage beginEditing];
    372     
    373     if (storedLineCount == INVALID_LINE_COUNT) {
    374         /* This usually indicates that our bytes per line or line number format changed, and we need to just recalculate everything */
    375         NSString *string = [self newLineStringForRange:HFRangeMake(startingLineIndex, linesRemaining)];
    376         [textStorage replaceCharactersInRange:NSMakeRange(0, [textStorage length]) withString:string];
    377         [string release];
    378         
    379     }
    380     else {
    381         HFRange leftRangeToReplace, rightRangeToReplace;
    382         HFRange leftRangeToStore, rightRangeToStore;
    383         
    384         HFRange oldRange = HFRangeMake(storedLineIndex, storedLineCount);
    385         HFRange newRange = HFRangeMake(startingLineIndex, linesRemaining);
    386         HFRange rangeToPreserve = HFIntersectionRange(oldRange, newRange);
    387         
    388         if (rangeToPreserve.length == 0) {
    389             leftRangeToReplace = oldRange;
    390             leftRangeToStore = newRange;
    391             rightRangeToReplace = HFZeroRange;
    392             rightRangeToStore = HFZeroRange;
    393         }
    394         else {
    395             if (debug) NSLog(@"Preserving %llu", rangeToPreserve.length);
    396             HFASSERT(HFRangeIsSubrangeOfRange(rangeToPreserve, newRange));
    397             HFASSERT(HFRangeIsSubrangeOfRange(rangeToPreserve, oldRange));
    398             const unsigned long long maxPreserve = HFMaxRange(rangeToPreserve);
    399             leftRangeToReplace = HFRangeMake(oldRange.location, rangeToPreserve.location - oldRange.location);
    400             leftRangeToStore = HFRangeMake(newRange.location, rangeToPreserve.location - newRange.location);
    401             rightRangeToReplace = HFRangeMake(maxPreserve, HFMaxRange(oldRange) - maxPreserve);
    402             rightRangeToStore = HFRangeMake(maxPreserve, HFMaxRange(newRange) - maxPreserve);
    403         }
    404         
    405         if (debug) NSLog(@"Changing %@ -> %@", HFRangeToString(oldRange), HFRangeToString(newRange));
    406         if (debug) NSLog(@"LEFT: %@ -> %@", HFRangeToString(leftRangeToReplace), HFRangeToString(leftRangeToStore));
    407         if (debug) NSLog(@"RIGHT: %@ -> %@", HFRangeToString(rightRangeToReplace), HFRangeToString(rightRangeToStore));
    408         
    409         HFASSERT(leftRangeToReplace.length == 0 || HFRangeIsSubrangeOfRange(leftRangeToReplace, oldRange));
    410         HFASSERT(rightRangeToReplace.length == 0 || HFRangeIsSubrangeOfRange(rightRangeToReplace, oldRange));
    411         
    412         if (leftRangeToReplace.length > 0 || leftRangeToStore.length > 0) {
    413             NSUInteger charactersToDelete = [self characterCountForLineRange:leftRangeToReplace];
    414             NSRange rangeToDelete = NSMakeRange(0, charactersToDelete);
    415             if (leftRangeToStore.length == 0) {
    416                 [textStorage deleteCharactersInRange:rangeToDelete];
    417                 if (debug) NSLog(@"Left deleting text range %@", NSStringFromRange(rangeToDelete));
    418             }
    419             else {
    420                 NSString *leftRangeString = [self newLineStringForRange:leftRangeToStore];
    421                 [textStorage replaceCharactersInRange:rangeToDelete withString:leftRangeString];
    422                 if (debug) NSLog(@"Replacing text range %@ with %@", NSStringFromRange(rangeToDelete), leftRangeString);
    423                 [leftRangeString release];
    424             }
    425         }
    426         
    427         if (rightRangeToReplace.length > 0 || rightRangeToStore.length > 0) {
    428             NSUInteger charactersToDelete = [self characterCountForLineRange:rightRangeToReplace];
    429             NSUInteger stringLength = [textStorage length];
    430             HFASSERT(charactersToDelete <= stringLength);
    431             NSRange rangeToDelete = NSMakeRange(stringLength - charactersToDelete, charactersToDelete);
    432             if (rightRangeToStore.length == 0) {
    433                 [textStorage deleteCharactersInRange:rangeToDelete];
    434                 if (debug) NSLog(@"Right deleting text range %@", NSStringFromRange(rangeToDelete));
    435             }
    436             else {
    437                 NSString *rightRangeString = [self newLineStringForRange:rightRangeToStore];
    438                 [textStorage replaceCharactersInRange:rangeToDelete withString:rightRangeString];
    439                 if (debug) NSLog(@"Replacing text range %@ with %@ (for range %@)", NSStringFromRange(rangeToDelete), rightRangeString, HFRangeToString(rightRangeToStore));
    440                 [rightRangeString release];
    441             }
    442         }
    443     }
    444     
    445     if (!textAttributes) {
    446         NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    447         [mutableStyle setAlignment:NSRightTextAlignment];
    448         NSParagraphStyle *paragraphStyle = [mutableStyle copy];
    449         [mutableStyle release];
    450         textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
    451         [paragraphStyle release];
    452         [textStorage setAttributes:textAttributes range:NSMakeRange(0, [textStorage length])];
    453     }
    454     
    455     [textStorage endEditing];
    456     
    457 #if ! NDEBUG
    458     NSString *comparisonString = [self newLineStringForRange:HFRangeMake(startingLineIndex, linesRemaining)];
    459     if (! [comparisonString isEqualToString:[textStorage string]]) {
    460         NSLog(@"Not equal!");
    461         NSLog(@"Expected:\n%@", comparisonString);
    462         NSLog(@"Actual:\n%@", [textStorage string]);
    463     }
    464     HFASSERT([comparisonString isEqualToString:[textStorage string]]);
    465     [comparisonString release];
    466 #endif
    467     
    468     storedLineIndex = startingLineIndex;
    469     storedLineCount = linesRemaining;
    470 }
    471 
    472 - (void)drawLineNumbersWithClipSingleStringDrawing:(NSRect)clipRect {
    473     USE(clipRect);
    474     unsigned long long lineIndex = HFFPToUL(floorl(_lineRangeToDraw.location));
    475     NSUInteger linesRemaining = ll2l(HFFPToUL(ceill(_lineRangeToDraw.length + _lineRangeToDraw.location) - floorl(_lineRangeToDraw.location)));
    476     
    477     CGFloat linesToVerticallyOffset = ld2f(_lineRangeToDraw.location - floorl(_lineRangeToDraw.location));
    478     CGFloat verticalOffset = linesToVerticallyOffset * _lineHeight + 1.5;
    479     NSRect textRect = self.bounds;
    480     textRect.size.width -= 5;
    481     textRect.origin.y -= verticalOffset;
    482     textRect.size.height += verticalOffset + _lineHeight;
    483     
    484     NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    485     [mutableStyle setAlignment:NSRightTextAlignment];
    486     [mutableStyle setMinimumLineHeight:_lineHeight];
    487     [mutableStyle setMaximumLineHeight:_lineHeight];
    488     NSParagraphStyle *paragraphStyle = [mutableStyle copy];
    489     [mutableStyle release];
    490     NSColor *color = [[NSColor textColor] colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
    491     color = [NSColor colorWithRed:color.redComponent green:color.greenComponent blue:color.blueComponent alpha:0.75];
    492     NSDictionary *_textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSFont fontWithName:_font.fontName size:9], NSFontAttributeName, color, NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
    493     [paragraphStyle release];
    494     
    495 
    496     NSString *string = [self newLineStringForRange:HFRangeMake(lineIndex, linesRemaining)];
    497     [string drawInRect:textRect withAttributes:_textAttributes];
    498     [string release];
    499     [_textAttributes release];
    500 }
    501 
    502 - (void)drawLineNumbersWithClipSingleStringCellDrawing:(NSRect)clipRect {
    503     USE(clipRect);
    504     const CGFloat cellTextContainerPadding = 2.f;
    505     unsigned long long lineIndex = HFFPToUL(floorl(_lineRangeToDraw.location));
    506     NSUInteger linesRemaining = ll2l(HFFPToUL(ceill(_lineRangeToDraw.length + _lineRangeToDraw.location) - floorl(_lineRangeToDraw.location)));
    507     
    508     CGFloat linesToVerticallyOffset = ld2f(_lineRangeToDraw.location - floorl(_lineRangeToDraw.location));
    509     CGFloat verticalOffset = linesToVerticallyOffset * _lineHeight + 1;
    510     NSRect textRect = self.bounds;
    511     textRect.size.width -= 5;
    512     textRect.origin.y -= verticalOffset;
    513     textRect.origin.x += cellTextContainerPadding;
    514     textRect.size.height += verticalOffset;
    515     
    516     if (! textAttributes) {
    517         NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    518         [mutableStyle setAlignment:NSRightTextAlignment];
    519         [mutableStyle setMinimumLineHeight:_lineHeight];
    520         [mutableStyle setMaximumLineHeight:_lineHeight];
    521         NSParagraphStyle *paragraphStyle = [mutableStyle copy];
    522         [mutableStyle release];
    523         textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
    524         [paragraphStyle release];
    525     }
    526     
    527     NSString *string = [self newLineStringForRange:HFRangeMake(lineIndex, linesRemaining)];
    528     NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:textAttributes];
    529     [string release];
    530     NSCell *cell = [[NSCell alloc] initTextCell:@""];
    531     [cell setAttributedStringValue:attributedString];
    532     [cell drawWithFrame:textRect inView:self];
    533     [[NSColor purpleColor] set];
    534     NSFrameRect(textRect);
    535     [cell release];
    536     [attributedString release];
    537 }
    538 
    539 - (void)drawLineNumbersWithClipFullLayoutManager:(NSRect)clipRect {
    540     USE(clipRect);
    541     unsigned long long lineIndex = HFFPToUL(floorl(_lineRangeToDraw.location));
    542     NSUInteger linesRemaining = ll2l(HFFPToUL(ceill(_lineRangeToDraw.length + _lineRangeToDraw.location) - floorl(_lineRangeToDraw.location)));
    543     if (lineIndex != storedLineIndex || linesRemaining != storedLineCount) {
    544         [self updateLayoutManagerWithLineIndex:lineIndex lineCount:linesRemaining];
    545     }
    546     
    547     CGFloat verticalOffset = ld2f(_lineRangeToDraw.location - floorl(_lineRangeToDraw.location));
    548     
    549     NSPoint textPoint = self.bounds.origin;
    550     textPoint.y -= verticalOffset * _lineHeight;
    551     [layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, [layoutManager numberOfGlyphs]) atPoint:textPoint];
    552 }
    553 
    554 - (void)drawLineNumbersWithClip:(NSRect)clipRect {
    555 #if TIME_LINE_NUMBERS
    556     CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    557 #endif
    558     NSInteger drawingMode = 3; // (useStringDrawingPath ? 1 : 3);
    559     switch (drawingMode) {
    560         // Drawing can't be done right if fonts are wider than expected, but all
    561         // of these have rather nasty behavior in that case. I've commented what
    562         // that behavior is; the comment is hypothetical 'could' if it shouldn't
    563         // actually be a problem in practice.
    564         // TODO: Make a drawing mode that is "Fonts could get clipped if too wide"
    565         //       because that seems like better behavior than any of these.
    566         case 0:
    567             // Most fonts are too wide and every character gets piled on right (unreadable).
    568             [self drawLineNumbersWithClipLayoutManagerPerLine:clipRect];
    569             break;
    570         case 1:
    571             // Last characters could get omitted (*not* clipped) if too wide.
    572             // Also, most fonts have bottoms clipped (very unsigntly).
    573             [self drawLineNumbersWithClipStringDrawing:clipRect];
    574             break;
    575         case 2:
    576             // Most fonts are too wide and wrap (breaks numbering).
    577             [self drawLineNumbersWithClipFullLayoutManager:clipRect];
    578             break;
    579         case 3:
    580             // Fonts could wrap if too wide (breaks numbering).
    581             // *Note that that this is the only mode that generally works.*
    582             [self drawLineNumbersWithClipSingleStringDrawing:clipRect];
    583             break;
    584         case 4:
    585             // Most fonts are too wide and wrap (breaks numbering).
    586             [self drawLineNumbersWithClipSingleStringCellDrawing:clipRect];
    587             break;
    588     }
    589 #if TIME_LINE_NUMBERS
    590     CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
    591     NSLog(@"Line number time: %f", endTime - startTime);
    592 #endif
    593 }
    594 
    595 - (void)drawRect:(NSRect)clipRect {
    596     [self drawDividerWithClip:clipRect];
    597     [self drawLineNumbersWithClip:clipRect];
    598 }
    599 
    600 - (void)setLineRangeToDraw:(HFFPRange)range {
    601     if (! HFFPRangeEqualsRange(range, _lineRangeToDraw)) {
    602         _lineRangeToDraw = range;
    603         [self setNeedsDisplay:YES];
    604     }
    605 }
    606 
    607 - (void)setBytesPerLine:(NSUInteger)val {
    608     if (_bytesPerLine != val) {
    609         _bytesPerLine = val;
    610         storedLineCount = INVALID_LINE_COUNT;
    611         [self setNeedsDisplay:YES];
    612     }
    613 }
    614 
    615 - (void)setLineNumberFormat:(HFLineNumberFormat)format {
    616     if (format != _lineNumberFormat) {
    617         _lineNumberFormat = format;
    618         storedLineCount = INVALID_LINE_COUNT;
    619         [self setNeedsDisplay:YES];
    620     }
    621 }
    622 
    623 - (BOOL)canUseStringDrawingPathForFont:(NSFont *)testFont {
    624     NSString *name = [testFont fontName];
    625     // No, Menlo does not work here.
    626     return [name isEqualToString:@"Monaco"] || [name isEqualToString:@"Courier"] || [name isEqualToString:@"Consolas"];
    627 }
    628 
    629 - (void)setFont:(NSFont *)val {
    630     if (val != _font) {
    631         [_font release];
    632         _font = [val copy];
    633         [textStorage deleteCharactersInRange:NSMakeRange(0, [textStorage length])]; //delete the characters so we know to set the font next time we render
    634         [textAttributes release];
    635         textAttributes = nil;
    636         storedLineCount = INVALID_LINE_COUNT;
    637         useStringDrawingPath = [self canUseStringDrawingPathForFont:_font];
    638         [self setNeedsDisplay:YES];
    639     }
    640 }
    641 
    642 - (void)setLineHeight:(CGFloat)height {
    643     if (_lineHeight != height) {
    644         _lineHeight = height;
    645         [self setNeedsDisplay:YES];
    646     }
    647 }
    648 
    649 - (void)setFrameSize:(NSSize)size {
    650     [super setFrameSize:size];
    651     [textContainer setContainerSize:NSMakeSize(self.bounds.size.width, [textContainer containerSize].height)];
    652 }
    653 
    654 - (void)mouseDown:(NSEvent *)event {
    655     USE(event);
    656     // [_representer cycleLineNumberFormat];
    657 }
    658 
    659 - (void)scrollWheel:(NSEvent *)scrollEvent {
    660     [_representer.controller scrollWithScrollEvent:scrollEvent];
    661 }
    662 
    663 + (NSUInteger)digitsRequiredToDisplayLineNumber:(unsigned long long)lineNumber inFormat:(HFLineNumberFormat)format {
    664     switch (format) {
    665         case HFLineNumberFormatDecimal: return HFCountDigitsBase10(lineNumber);
    666 #if HEX_LINE_NUMBERS_HAVE_0X_PREFIX
    667         case HFLineNumberFormatHexadecimal: return 2 + HFCountDigitsBase16(lineNumber);
    668 #else
    669         case HFLineNumberFormatHexadecimal: return HFCountDigitsBase16(lineNumber);
    670 #endif
    671         default: return 0;
    672     }
    673 }
    674 
    675 /* Compatibility with Sonoma */
    676 + (bool)clipsToBounds
    677 {
    678     return true;
    679 }
    680 
    681 @end

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.