SameBoy/HexFiend/HFTextRepresenter.m

378 lines
14 KiB
Objective-C

//
// HFTextRepresenter.m
// HexFiend_2
//
// Copyright 2007 ridiculous_fish. All rights reserved.
//
#import <HexFiend/HFTextRepresenter_Internal.h>
#import <HexFiend/HFRepresenterTextView.h>
#import <HexFiend/HFPasteboardOwner.h>
#import <HexFiend/HFByteArray.h>
#import <HexFiend/HFTextVisualStyleRun.h>
@implementation HFTextRepresenter
- (Class)_textViewClass {
UNIMPLEMENTED();
}
- (instancetype)init {
self = [super init];
if (@available(macOS 10.14, *)) {
_rowBackgroundColors = [[NSColor alternatingContentBackgroundColors] retain];
} else {
NSColor *color1 = [NSColor windowBackgroundColor];
NSColor *color2 = [NSColor colorWithDeviceWhite:0.96 alpha:1];
_rowBackgroundColors = [@[color1, color2] retain];
}
return self;
}
- (void)dealloc {
if ([self isViewLoaded]) {
[[self view] clearRepresenter];
}
[_rowBackgroundColors release];
[super dealloc];
}
- (void)encodeWithCoder:(NSCoder *)coder {
HFASSERT([coder allowsKeyedCoding]);
[super encodeWithCoder:coder];
[coder encodeBool:_behavesAsTextField forKey:@"HFBehavesAsTextField"];
[coder encodeObject:_rowBackgroundColors forKey:@"HFRowBackgroundColors"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
HFASSERT([coder allowsKeyedCoding]);
self = [super initWithCoder:coder];
_behavesAsTextField = [coder decodeBoolForKey:@"HFBehavesAsTextField"];
_rowBackgroundColors = [[coder decodeObjectForKey:@"HFRowBackgroundColors"] retain];
return self;
}
- (NSView *)createView {
HFRepresenterTextView *view = [[[self _textViewClass] alloc] initWithRepresenter:self];
[view setAutoresizingMask:NSViewHeightSizable];
return view;
}
- (HFByteArrayDataStringType)byteArrayDataStringType {
UNIMPLEMENTED();
}
- (HFRange)entireDisplayedRange {
HFController *controller = [self controller];
unsigned long long contentsLength = [controller contentsLength];
HFASSERT(controller != NULL);
HFFPRange displayedLineRange = [controller displayedLineRange];
NSUInteger bytesPerLine = [controller bytesPerLine];
unsigned long long lineStart = HFFPToUL(floorl(displayedLineRange.location));
unsigned long long lineEnd = HFFPToUL(ceill(displayedLineRange.location + displayedLineRange.length));
HFASSERT(lineEnd >= lineStart);
HFRange byteRange = HFRangeMake(HFProductULL(bytesPerLine, lineStart), HFProductULL(lineEnd - lineStart, bytesPerLine));
if (byteRange.length == 0) {
/* This can happen if we are too small to even show one line */
return HFRangeMake(0, 0);
}
else {
HFASSERT(byteRange.location <= contentsLength);
byteRange.length = MIN(byteRange.length, contentsLength - byteRange.location);
HFASSERT(HFRangeIsSubrangeOfRange(byteRange, HFRangeMake(0, [controller contentsLength])));
return byteRange;
}
}
- (NSRect)furthestRectOnEdge:(NSRectEdge)edge forByteRange:(HFRange)byteRange {
HFASSERT(byteRange.length > 0);
HFRange displayedRange = [self entireDisplayedRange];
HFRange intersection = HFIntersectionRange(displayedRange, byteRange);
NSRect result = {{0,},};
if (intersection.length > 0) {
NSRange intersectionNSRange = NSMakeRange(ll2l(intersection.location - displayedRange.location), ll2l(intersection.length));
if (intersectionNSRange.length > 0) {
result = [[self view] furthestRectOnEdge:edge forRange:intersectionNSRange];
}
}
else if (byteRange.location < displayedRange.location) {
/* We're below it. */
return NSMakeRect(-CGFLOAT_MAX, -CGFLOAT_MAX, 0, 0);
}
else if (byteRange.location >= HFMaxRange(displayedRange)) {
/* We're above it */
return NSMakeRect(CGFLOAT_MAX, CGFLOAT_MAX, 0, 0);
}
else {
/* Shouldn't be possible to get here */
[NSException raise:NSInternalInconsistencyException format:@"furthestRectOnEdge: expected an intersection, or a range below or above the byte range, but nothin'"];
}
return result;
}
- (NSPoint)locationOfCharacterAtByteIndex:(unsigned long long)index {
NSPoint result;
HFRange displayedRange = [self entireDisplayedRange];
if (HFLocationInRange(index, displayedRange) || index == HFMaxRange(displayedRange)) {
NSUInteger location = ll2l(index - displayedRange.location);
result = [[self view] originForCharacterAtByteIndex:location];
}
else if (index < displayedRange.location) {
result = NSMakePoint(-CGFLOAT_MAX, -CGFLOAT_MAX);
}
else {
result = NSMakePoint(CGFLOAT_MAX, CGFLOAT_MAX);
}
return result;
}
- (HFTextVisualStyleRun *)styleForAttributes:(NSSet *)attributes range:(NSRange)range {
HFTextVisualStyleRun *run = [[[HFTextVisualStyleRun alloc] init] autorelease];
[run setRange:range];
[run setForegroundColor:[NSColor blackColor]];
return run;
}
- (NSArray *)stylesForRange:(HFRange)range {
return nil;
}
- (void)updateText {
HFController *controller = [self controller];
HFRepresenterTextView *view = [self view];
HFRange entireDisplayedRange = [self entireDisplayedRange];
[view setData:[controller dataForRange:entireDisplayedRange]];
[view setStyles:[self stylesForRange:entireDisplayedRange]];
HFFPRange lineRange = [controller displayedLineRange];
long double offsetLongDouble = lineRange.location - floorl(lineRange.location);
CGFloat offset = ld2f(offsetLongDouble);
[view setVerticalOffset:offset];
[view setStartingLineBackgroundColorIndex:ll2l(HFFPToUL(floorl(lineRange.location)) % NSUIntegerMax)];
}
- (void)initializeView {
[super initializeView];
HFRepresenterTextView *view = [self view];
HFController *controller = [self controller];
if (controller) {
[view setFont:[controller font]];
[view setEditable:[controller editable]];
[self updateText];
}
else {
[view setFont:[NSFont fontWithName:HFDEFAULT_FONT size:HFDEFAULT_FONTSIZE]];
}
}
- (void)scrollWheel:(NSEvent *)event {
[[self controller] scrollWithScrollEvent:event];
}
- (void)selectAll:(id)sender {
[[self controller] selectAll:sender];
}
- (double)selectionPulseAmount {
return [[self controller] selectionPulseAmount];
}
- (void)controllerDidChange:(HFControllerPropertyBits)bits {
if (bits & (HFControllerFont | HFControllerLineHeight)) {
[[self view] setFont:[[self controller] font]];
}
if (bits & (HFControllerContentValue | HFControllerDisplayedLineRange | HFControllerByteRangeAttributes)) {
[self updateText];
}
if (bits & (HFControllerSelectedRanges | HFControllerDisplayedLineRange)) {
[[self view] updateSelectedRanges];
}
if (bits & (HFControllerEditable)) {
[[self view] setEditable:[[self controller] editable]];
}
if (bits & (HFControllerAntialias)) {
[[self view] setShouldAntialias:[[self controller] shouldAntialias]];
}
if (bits & (HFControllerShowCallouts)) {
[[self view] setShouldDrawCallouts:[[self controller] shouldShowCallouts]];
}
if (bits & (HFControllerColorBytes)) {
if([[self controller] shouldColorBytes]) {
[[self view] setByteColoring: ^(uint8_t byte, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a){
*r = *g = *b = (uint8_t)(255 * ((255-byte)/255.0*0.6+0.4));
*a = (uint8_t)(255 * 0.7);
}];
} else {
[[self view] setByteColoring:NULL];
}
}
[super controllerDidChange:bits];
}
- (double)maximumAvailableLinesForViewHeight:(CGFloat)viewHeight {
return [[self view] maximumAvailableLinesForViewHeight:viewHeight];
}
- (NSUInteger)maximumBytesPerLineForViewWidth:(CGFloat)viewWidth {
return [[self view] maximumBytesPerLineForViewWidth:viewWidth];
}
- (CGFloat)minimumViewWidthForBytesPerLine:(NSUInteger)bytesPerLine {
return [[self view] minimumViewWidthForBytesPerLine:bytesPerLine];
}
- (NSUInteger)byteGranularity {
HFRepresenterTextView *view = [self view];
NSUInteger bytesPerColumn = MAX([view bytesPerColumn], 1u), bytesPerCharacter = [view bytesPerCharacter];
return HFLeastCommonMultiple(bytesPerColumn, bytesPerCharacter);
}
- (NSArray *)displayedSelectedContentsRanges {
HFController *controller = [self controller];
NSArray *result;
NSArray *selectedRanges = [controller selectedContentsRanges];
HFRange displayedRange = [self entireDisplayedRange];
HFASSERT(displayedRange.length <= NSUIntegerMax);
NEW_ARRAY(NSValue *, clippedSelectedRanges, [selectedRanges count]);
NSUInteger clippedRangeIndex = 0;
FOREACH(HFRangeWrapper *, wrapper, selectedRanges) {
HFRange selectedRange = [wrapper HFRange];
BOOL clippedRangeIsVisible;
NSRange clippedSelectedRange = {0,};
/* Necessary because zero length ranges do not intersect anything */
if (selectedRange.length == 0) {
/* Remember that {6, 0} is considered a subrange of {3, 3} */
clippedRangeIsVisible = HFRangeIsSubrangeOfRange(selectedRange, displayedRange);
if (clippedRangeIsVisible) {
HFASSERT(selectedRange.location >= displayedRange.location);
clippedSelectedRange.location = ll2l(selectedRange.location - displayedRange.location);
clippedSelectedRange.length = 0;
}
}
else {
// selectedRange.length > 0
clippedRangeIsVisible = HFIntersectsRange(selectedRange, displayedRange);
if (clippedRangeIsVisible) {
HFRange intersectionRange = HFIntersectionRange(selectedRange, displayedRange);
HFASSERT(intersectionRange.location >= displayedRange.location);
clippedSelectedRange.location = ll2l(intersectionRange.location - displayedRange.location);
clippedSelectedRange.length = ll2l(intersectionRange.length);
}
}
if (clippedRangeIsVisible) clippedSelectedRanges[clippedRangeIndex++] = [NSValue valueWithRange:clippedSelectedRange];
}
result = [NSArray arrayWithObjects:clippedSelectedRanges count:clippedRangeIndex];
FREE_ARRAY(clippedSelectedRanges);
return result;
}
//maps bookmark keys as NSNumber to byte locations as NSNumbers. Because bookmark callouts may extend beyond the lines containing them, allow a larger range by 10 lines.
- (NSDictionary *)displayedBookmarkLocations {
NSMutableDictionary *result = nil;
HFController *controller = [self controller];
NSUInteger rangeExtension = 10 * [controller bytesPerLine];
HFRange displayedRange = [self entireDisplayedRange];
HFRange includedRange = displayedRange;
/* Extend the bottom */
unsigned long long bottomExtension = MIN(includedRange.location, rangeExtension);
includedRange.location -= bottomExtension;
includedRange.length += bottomExtension;
/* Extend the top */
unsigned long long topExtension = MIN([controller contentsLength] - HFMaxRange(includedRange), rangeExtension);
includedRange.length = HFSum(includedRange.length, topExtension);
return result;
}
- (unsigned long long)byteIndexForCharacterIndex:(NSUInteger)characterIndex {
HFController *controller = [self controller];
HFFPRange lineRange = [controller displayedLineRange];
unsigned long long scrollAmount = HFFPToUL(floorl(lineRange.location));
unsigned long long byteIndex = HFProductULL(scrollAmount, [controller bytesPerLine]) + characterIndex * [[self view] bytesPerCharacter];
return byteIndex;
}
- (void)beginSelectionWithEvent:(NSEvent *)event forCharacterIndex:(NSUInteger)characterIndex {
[[self controller] beginSelectionWithEvent:event forByteIndex:[self byteIndexForCharacterIndex:characterIndex]];
}
- (void)continueSelectionWithEvent:(NSEvent *)event forCharacterIndex:(NSUInteger)characterIndex {
[[self controller] continueSelectionWithEvent:event forByteIndex:[self byteIndexForCharacterIndex:characterIndex]];
}
- (void)endSelectionWithEvent:(NSEvent *)event forCharacterIndex:(NSUInteger)characterIndex {
[[self controller] endSelectionWithEvent:event forByteIndex:[self byteIndexForCharacterIndex:characterIndex]];
}
- (void)insertText:(NSString *)text {
USE(text);
UNIMPLEMENTED_VOID();
}
- (void)copySelectedBytesToPasteboard:(NSPasteboard *)pb {
USE(pb);
UNIMPLEMENTED_VOID();
}
- (void)cutSelectedBytesToPasteboard:(NSPasteboard *)pb {
[self copySelectedBytesToPasteboard:pb];
[[self controller] deleteSelection];
}
- (NSData *)dataFromPasteboardString:(NSString *)string {
USE(string);
UNIMPLEMENTED();
}
- (BOOL)canPasteFromPasteboard:(NSPasteboard *)pb {
REQUIRE_NOT_NULL(pb);
if ([[self controller] editable]) {
// we can paste if the pboard contains text or contains an HFByteArray
return [HFPasteboardOwner unpackByteArrayFromPasteboard:pb] || [pb availableTypeFromArray:@[NSStringPboardType]];
}
return NO;
}
- (BOOL)canCut {
/* We can cut if we are editable, we have at least one byte selected, and we are not in overwrite mode */
HFController *controller = [self controller];
if ([controller editMode] != HFInsertMode) return NO;
if (! [controller editable]) return NO;
FOREACH(HFRangeWrapper *, rangeWrapper, [controller selectedContentsRanges]) {
if ([rangeWrapper HFRange].length > 0) return YES; //we have something selected
}
return NO; // we did not find anything selected
}
- (BOOL)pasteBytesFromPasteboard:(NSPasteboard *)pb {
REQUIRE_NOT_NULL(pb);
BOOL result = NO;
HFByteArray *byteArray = [HFPasteboardOwner unpackByteArrayFromPasteboard:pb];
if (byteArray) {
[[self controller] insertByteArray:byteArray replacingPreviousBytes:0 allowUndoCoalescing:NO];
result = YES;
}
else {
NSString *stringType = [pb availableTypeFromArray:@[NSStringPboardType]];
if (stringType) {
NSString *stringValue = [pb stringForType:stringType];
if (stringValue) {
NSData *data = [self dataFromPasteboardString:stringValue];
if (data) {
[[self controller] insertData:data replacingPreviousBytes:0 allowUndoCoalescing:NO];
}
}
}
}
return result;
}
@end