SameBoy/HexFiend/HFLineCountingRepresenter.m

251 lines
8.6 KiB
Objective-C

//
// HFLineCountingRepresenter.m
// HexFiend_2
//
// Copyright 2007 ridiculous_fish. All rights reserved.
//
#import <HexFiend/HFLineCountingRepresenter.h>
#import <HexFiend/HFLineCountingView.h>
NSString *const HFLineCountingRepresenterMinimumViewWidthChanged = @"HFLineCountingRepresenterMinimumViewWidthChanged";
/* Returns the maximum advance in points for a hexadecimal digit for the given font (interpreted as a screen font) */
static CGFloat maximumDigitAdvanceForFont(NSFont *font) {
REQUIRE_NOT_NULL(font);
font = [font screenFont];
CGFloat maxDigitAdvance = 0;
NSDictionary *attributesDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:font, NSFontAttributeName, nil];
NSTextStorage *storage = [[NSTextStorage alloc] init];
NSLayoutManager *manager = [[NSLayoutManager alloc] init];
[storage setFont:font];
[storage addLayoutManager:manager];
NSSize advancements[16] = {};
NSGlyph glyphs[16];
/* Generate a glyph for every hex digit */
for (NSUInteger i=0; i < 16; i++) {
char c = "0123456789ABCDEF"[i];
NSString *string = [[NSString alloc] initWithBytes:&c length:1 encoding:NSASCIIStringEncoding];
[storage replaceCharactersInRange:NSMakeRange(0, (i ? 1 : 0)) withString:string];
[string release];
glyphs[i] = [manager glyphAtIndex:0 isValidIndex:NULL];
HFASSERT(glyphs[i] != NSNullGlyph);
}
/* Get the advancements of each of those glyphs */
[font getAdvancements:advancements forGlyphs:glyphs count:sizeof glyphs / sizeof *glyphs];
[manager release];
[attributesDictionary release];
[storage release];
/* Find the widest digit */
for (NSUInteger i=0; i < sizeof glyphs / sizeof *glyphs; i++) {
maxDigitAdvance = HFMax(maxDigitAdvance, advancements[i].width);
}
return maxDigitAdvance;
}
@implementation HFLineCountingRepresenter
- (instancetype)init {
if ((self = [super init])) {
minimumDigitCount = 2;
digitsToRepresentContentsLength = minimumDigitCount;
interiorShadowEdge = NSMaxXEdge;
_borderedEdges = (1 << NSMaxXEdge);
_borderColor = [[NSColor darkGrayColor] retain];
_backgroundColor = [[NSColor colorWithCalibratedWhite:(CGFloat).87 alpha:1] retain];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
HFASSERT([coder allowsKeyedCoding]);
[super encodeWithCoder:coder];
[coder encodeDouble:lineHeight forKey:@"HFLineHeight"];
[coder encodeInt64:minimumDigitCount forKey:@"HFMinimumDigitCount"];
[coder encodeInt64:lineNumberFormat forKey:@"HFLineNumberFormat"];
[coder encodeObject:self.backgroundColor forKey:@"HFBackgroundColor"];
[coder encodeObject:self.borderColor forKey:@"HFBorderColor"];
[coder encodeInt64:self.borderedEdges forKey:@"HFBorderedEdges"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
HFASSERT([coder allowsKeyedCoding]);
self = [super initWithCoder:coder];
lineHeight = (CGFloat)[coder decodeDoubleForKey:@"HFLineHeight"];
minimumDigitCount = (NSUInteger)[coder decodeInt64ForKey:@"HFMinimumDigitCount"];
lineNumberFormat = (HFLineNumberFormat)[coder decodeInt64ForKey:@"HFLineNumberFormat"];
_borderedEdges = [coder decodeObjectForKey:@"HFBorderedEdges"] ? (NSInteger)[coder decodeInt64ForKey:@"HFBorderedEdges"] : 0;
_borderColor = [[coder decodeObjectForKey:@"HFBorderColor"] ?: [NSColor darkGrayColor] retain];
_backgroundColor = [[coder decodeObjectForKey:@"HFBackgroundColor"] ?: [NSColor colorWithCalibratedWhite:(CGFloat).87 alpha:1] retain];
return self;
}
- (void)dealloc {
[_borderColor release];
[_backgroundColor release];
[super dealloc];
}
- (NSView *)createView {
HFLineCountingView *result = [[HFLineCountingView alloc] initWithFrame:NSMakeRect(0, 0, 60, 10)];
[result setRepresenter:self];
[result setAutoresizingMask:NSViewHeightSizable];
return result;
}
- (void)postMinimumViewWidthChangedNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:HFLineCountingRepresenterMinimumViewWidthChanged object:self];
}
- (void)updateDigitAdvanceWithFont:(NSFont *)font {
CGFloat newDigitAdvance = maximumDigitAdvanceForFont(font);
if (digitAdvance != newDigitAdvance) {
digitAdvance = newDigitAdvance;
[self postMinimumViewWidthChangedNotification];
}
}
- (void)updateFontAndLineHeight {
HFLineCountingView *view = [self view];
HFController *controller = [self controller];
NSFont *font = controller ? [controller font] : [NSFont fontWithName:HFDEFAULT_FONT size:HFDEFAULT_FONTSIZE];
[view setFont:font];
[view setLineHeight: controller ? [controller lineHeight] : HFDEFAULT_FONTSIZE];
[self updateDigitAdvanceWithFont:font];
}
- (void)updateLineNumberFormat {
[[self view] setLineNumberFormat:lineNumberFormat];
}
- (void)updateBytesPerLine {
[[self view] setBytesPerLine:[[self controller] bytesPerLine]];
}
- (void)updateLineRangeToDraw {
HFFPRange lineRange = {0, 0};
HFController *controller = [self controller];
if (controller) {
lineRange = [controller displayedLineRange];
}
[[self view] setLineRangeToDraw:lineRange];
}
- (CGFloat)preferredWidth {
if (digitAdvance == 0) {
/* This may happen if we were loaded from a nib. We are lazy about fetching the controller's font to avoid ordering issues with nib unarchival. */
[self updateFontAndLineHeight];
}
return (CGFloat)10. + digitsToRepresentContentsLength * digitAdvance;
}
- (void)updateMinimumViewWidth {
HFController *controller = [self controller];
if (controller) {
unsigned long long contentsLength = [controller contentsLength];
NSUInteger bytesPerLine = [controller bytesPerLine];
/* We want to know how many lines are displayed. That's equal to the contentsLength divided by bytesPerLine rounded down, except in the case that we're at the end of a line, in which case we need to show one more. Hence adding 1 and dividing gets us the right result. */
unsigned long long lineCount = contentsLength / bytesPerLine;
unsigned long long contentsLengthRoundedToLine = HFProductULL(lineCount, bytesPerLine) - 1;
NSUInteger digitCount = [HFLineCountingView digitsRequiredToDisplayLineNumber:contentsLengthRoundedToLine inFormat:lineNumberFormat];
NSUInteger digitWidth = MAX(minimumDigitCount, digitCount);
if (digitWidth != digitsToRepresentContentsLength) {
digitsToRepresentContentsLength = digitWidth;
[self postMinimumViewWidthChangedNotification];
}
}
}
- (CGFloat)minimumViewWidthForBytesPerLine:(NSUInteger)bytesPerLine {
USE(bytesPerLine);
return [self preferredWidth];
}
- (HFLineNumberFormat)lineNumberFormat {
return lineNumberFormat;
}
- (void)setLineNumberFormat:(HFLineNumberFormat)format {
HFASSERT(format < HFLineNumberFormatMAXIMUM);
lineNumberFormat = format;
[self updateLineNumberFormat];
[self updateMinimumViewWidth];
}
- (void)cycleLineNumberFormat {
lineNumberFormat = (lineNumberFormat + 1) % HFLineNumberFormatMAXIMUM;
[self updateLineNumberFormat];
[self updateMinimumViewWidth];
}
- (void)initializeView {
[self updateFontAndLineHeight];
[self updateLineNumberFormat];
[self updateBytesPerLine];
[self updateLineRangeToDraw];
[self updateMinimumViewWidth];
}
- (void)controllerDidChange:(HFControllerPropertyBits)bits {
if (bits & HFControllerDisplayedLineRange) [self updateLineRangeToDraw];
if (bits & HFControllerBytesPerLine) [self updateBytesPerLine];
if (bits & (HFControllerFont | HFControllerLineHeight)) [self updateFontAndLineHeight];
if (bits & (HFControllerContentLength)) [self updateMinimumViewWidth];
}
- (void)setMinimumDigitCount:(NSUInteger)width {
minimumDigitCount = width;
[self updateMinimumViewWidth];
}
- (NSUInteger)minimumDigitCount {
return minimumDigitCount;
}
- (NSUInteger)digitCount {
return digitsToRepresentContentsLength;
}
+ (NSPoint)defaultLayoutPosition {
return NSMakePoint(-1, 0);
}
- (void)setInteriorShadowEdge:(NSInteger)edge {
self->interiorShadowEdge = edge;
if ([self isViewLoaded]) {
[[self view] setNeedsDisplay:YES];
}
}
- (NSInteger)interiorShadowEdge {
return interiorShadowEdge;
}
- (void)setBorderColor:(NSColor *)color {
[_borderColor autorelease];
_borderColor = [color copy];
if ([self isViewLoaded]) {
[[self view] setNeedsDisplay:YES];
}
}
- (void)setBackgroundColor:(NSColor *)color {
[_backgroundColor autorelease];
_backgroundColor = [color copy];
if ([self isViewLoaded]) {
[[self view] setNeedsDisplay:YES];
}
}
@end