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.