// // HFTextRepresenter.m // HexFiend_2 // // Copyright 2007 ridiculous_fish. All rights reserved. // #import #import #import #import #import @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