251 lines
8.6 KiB
Objective-C
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
|