1/* 2 * DiffTextView.m 3 * 4 * Copyright (c) 2002 Pierre-Yves Rivaille <pyrivail@ens-lyon.fr> 5 * 6 * This file is part of EasyDiff.app. 7 * 8 * EasyDiff.app is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * EasyDiff.app is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with EasyDiff.app; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24 25#import "DiffTextView.h" 26#include <math.h> 27 28//static NSRect rectsToFill[500]; 29 30@implementation DiffTextView 31 32- (id) initWithFrame: (NSRect) aRect 33{ 34 [super initWithFrame: aRect]; 35 36 lineRangesArray = nil; 37 blockCharacterRangesArray = [[NSMutableArray alloc] init]; 38 blockRectsArray = NSZoneMalloc([self zone], sizeof(NSRect)*10); 39 blockRectsArraySize = 10; 40 blockRectsArrayFirstFree = 0; 41 lastLine = 0; 42 firstCharOfLastLine = 0; 43 44 [self setEditable: NO]; 45 [self setSelectable: NO]; 46 [self setDrawsBackground: NO]; 47 48 return self; 49} 50 51- (void) dealloc 52{ 53 TEST_RELEASE(lineRangesArray); 54 TEST_RELEASE(changes); 55 TEST_RELEASE(colors); 56 [super dealloc]; 57} 58 59- (void) setChanges: (NSArray *)anArray 60{ 61 int i; 62 int count; 63 if (changes) 64 RELEASE(changes); 65 changes = RETAIN(anArray); 66 67 TEST_RELEASE(colors); 68 69 count = [changes count] / 2; 70 71 colors = [[NSMutableArray alloc] initWithCapacity: count]; 72 73 for (i = 0; i < count; i++) 74 { 75 [colors addObject: [NSColor colorWithDeviceRed:0.7 76 green:0.7 77 blue:1. 78 alpha:1.]]; 79 } 80 81 // NSLog(@"changes count %d", [changes count]); 82} 83 84- (void) setColor: (NSColor *) aColor 85 forChangeNumber: (int) number 86{ 87 if (colors == nil) 88 return; 89 90 [colors replaceObjectAtIndex: number 91 withObject: aColor]; 92 93 blockRectsArray[number].size.width = [self visibleRect].size.width; 94 blockRectsArray[number].origin.x = [self visibleRect].origin.x; 95 96 [self setNeedsDisplayInRect: 97 blockRectsArray[number]]; 98} 99 100- (void) computeLineRangesFromUpTo: (int) number 101{ 102 NSArray *array; 103 int count; 104 105 NSLayoutManager *lm = [self layoutManager]; 106 NSTextContainer *tc = [self textContainer]; 107 108 array = changes; 109 110 count = [array count]; 111 112 NSDebugLog(@"count %d", count); 113 { 114 NSString *internalString = [self string]; 115 int startLine; 116 int endLine; 117 int currentLine; 118 unsigned int firstCharOfLine = firstCharOfLastLine; 119 unsigned int firstCharOfNextLine; 120 int i; 121 int a, b; 122 123 separationPosition = (float *) 124 NSZoneMalloc([self zone], sizeof(float) * 125 (count + 2)); 126 127 separationDiff = (float *) 128 NSZoneMalloc([self zone], sizeof(float) * 129 (count + 1)); 130 131 separationPosition[0] = 0; 132 133 currentLine = lastLine; 134 for (i = 0; i < count; i += 2) 135 { 136 startLine = [[array objectAtIndex: i] intValue]; 137 endLine = [[array objectAtIndex: i+1] intValue] - 1; 138 139 while (currentLine < startLine) 140 { 141 [internalString getLineStart: NULL 142 end: &firstCharOfNextLine 143 contentsEnd: NULL 144 forRange: NSMakeRange(firstCharOfLine, 0)]; 145 currentLine ++; 146 firstCharOfLine = firstCharOfNextLine; 147 } 148 149 a = firstCharOfLine; 150 [blockCharacterRangesArray 151 addObject: [NSNumber numberWithInt: firstCharOfLine]]; 152 153 while (currentLine <= endLine) 154 { 155 [internalString getLineStart: NULL 156 end: &firstCharOfNextLine 157 contentsEnd: NULL 158 forRange: NSMakeRange(firstCharOfLine, 0)]; 159 currentLine ++; 160 firstCharOfLine = firstCharOfNextLine; 161 } 162 163 b = firstCharOfLine - 1; 164 165 [blockCharacterRangesArray 166 addObject: [NSNumber numberWithInt: firstCharOfLine - 1]]; 167 168 // NSLog(@"%d %d", blockRectsArraySize, blockRectsArrayFirstFree); 169 if (blockRectsArraySize == blockRectsArrayFirstFree) 170 { 171 blockRectsArray = 172 NSZoneRealloc([self zone], 173 blockRectsArray, 174 blockRectsArraySize * 2 * sizeof(NSRect)); 175 blockRectsArraySize *= 2; 176 } 177 178 // NSLog(@"a=%d b=%d", a, b); 179 180 if (a == [internalString length]) 181 { 182 blockRectsArray[blockRectsArrayFirstFree++] = 183 [lm boundingRectForGlyphRange: 184 [lm glyphRangeForCharacterRange: 185 NSMakeRange(a-1, 1) 186 actualCharacterRange:NULL] 187 inTextContainer:tc]; 188 blockRectsArray[blockRectsArrayFirstFree - 1].origin.y = 189 NSMaxY(blockRectsArray[blockRectsArrayFirstFree - 1]); 190 blockRectsArray[blockRectsArrayFirstFree - 1].size.height = 0; 191 192 separationPosition[i + 1] = 193 NSMinY(blockRectsArray[blockRectsArrayFirstFree - 1]); 194 195 separationPosition[i + 2] = 196 NSMinY(blockRectsArray[blockRectsArrayFirstFree - 1]); 197 198 separationDiff[i] = separationPosition[i+1] - 199 separationPosition[i]; 200 201 separationDiff[i+1] = separationPosition[i+2] - 202 separationPosition[i+1]; 203 204 blockRectsArray[blockRectsArrayFirstFree - 1].size.height = 2; 205 blockRectsArray[blockRectsArrayFirstFree - 1].origin.y--; 206 } 207 else if (a >= b) 208 { 209 blockRectsArray[blockRectsArrayFirstFree++] = 210 [lm boundingRectForGlyphRange: 211 [lm glyphRangeForCharacterRange: 212 NSMakeRange(a, 1) 213 actualCharacterRange:NULL] 214 inTextContainer:tc]; 215 216 separationPosition[i + 1] = 217 NSMinY(blockRectsArray[blockRectsArrayFirstFree - 1]); 218 219 separationPosition[i + 2] = 220 NSMinY(blockRectsArray[blockRectsArrayFirstFree - 1]); 221 222 NSDebugLog(@"a %d %f", i + 1, separationPosition[i + 1]); 223 NSDebugLog(@"a %d %f", i + 2, separationPosition[i + 2]); 224 225 separationDiff[i] = separationPosition[i+1] - 226 separationPosition[i]; 227 228 separationDiff[i+1] = separationPosition[i+2] - 229 separationPosition[i+1]; 230 231 232 blockRectsArray[blockRectsArrayFirstFree - 1].size.height = 2; 233 blockRectsArray[blockRectsArrayFirstFree - 1].origin.y--; 234 } 235 else 236 { 237 blockRectsArray[blockRectsArrayFirstFree++] = 238 [lm boundingRectForGlyphRange: 239 [lm glyphRangeForCharacterRange: 240 NSMakeRange(a, b - a) 241 actualCharacterRange:NULL] 242 inTextContainer:tc]; 243 244 separationPosition[i + 1] = 245 NSMinY(blockRectsArray[blockRectsArrayFirstFree - 1]); 246 247 separationPosition[i + 2] = 248 NSMaxY(blockRectsArray[blockRectsArrayFirstFree - 1]); 249 250 251 NSDebugLog(@"b %d %f", i + 1, separationPosition[i + 1]); 252 NSDebugLog(@"b %d %f", i + 2, separationPosition[i + 2]); 253 254 separationDiff[i] = separationPosition[i+1] - 255 separationPosition[i]; 256 257 separationDiff[i+1] = separationPosition[i+2] - 258 separationPosition[i+1]; 259 } 260 261 262 // NSLog(@"%@", NSStringFromRect 263 // (blockRectsArray[blockRectsArrayFirstFree-1])); 264 265 } 266 267 separationPosition[count + 1] = NSMaxY([self bounds]); 268 separationDiff[count] = separationPosition[count + 1] - 269 separationPosition[count]; 270 271 firstCharOfLastLine = firstCharOfLine; 272 lastLine = currentLine; 273 274 275 // NSLog(@"%d %@", [blockCharacterRangesArray count], 276 // [blockCharacterRangesArray description]); 277 } 278 279 { 280 281 } 282 283} 284 285/* 286- (void) computeAllLineRanges 287{ 288 NSString *internalString = [self string]; 289 int end; 290 int len = [internalString length]; 291 292 lineRangesArray = [[NSMutableArray alloc] init]; 293 [lineRangesArray addObject: 294 [NSNumber numberWithInt: -1]]; 295 296 end = -1; 297 while (end < len - 1) 298 { 299 [internalString getLineStart: NULL 300 end: NULL 301 contentsEnd: &end 302 forRange: NSMakeRange(end + 1, 0)]; 303 if (end >= len) 304 { 305 [lineRangesArray addObject: 306 [NSNumber numberWithInt: len - 1]]; 307 } 308 else 309 { 310 [lineRangesArray addObject: 311 [NSNumber numberWithInt: end]]; 312 } 313 } 314} 315*/ 316 317- (void) setLineRanges: (NSArray *) lineRanges 318{ 319 lineRangesArray = RETAIN(lineRanges); 320 321 // NSLog(@"lineRanges count %d", [lineRanges count]); 322 323} 324 325- (NSArray *) lineRangesArray 326{ 327 return lineRangesArray; 328} 329 330- (NSRange) lineRangesForRect: (NSRect) aRect 331{ 332 int i, count; 333 int startLine = -1; 334 int endLine = -1; 335 336 NSRange glyphRange, characterRange; 337 NSLayoutManager *lm = [self layoutManager]; 338 NSTextContainer *tc = [self textContainer]; 339 340 glyphRange = [lm glyphRangeForBoundingRect: aRect 341 inTextContainer: tc]; 342 characterRange = [lm characterRangeForGlyphRange: glyphRange 343 actualGlyphRange: NULL]; 344 345 count = [lineRangesArray count]; 346 i = 0; 347 while (i < count) 348 { 349 if ((int) characterRange.location <= 350 [[lineRangesArray objectAtIndex: i] intValue] + 1) 351 { 352 startLine = i; 353 break; 354 } 355 i++; 356 } 357 358 while (i < count) 359 { 360 if ((int) NSMaxRange(characterRange) <= 361 [[lineRangesArray objectAtIndex: i] intValue] + 1) 362 { 363 endLine = i; 364 break; 365 } 366 i++; 367 } 368 369 /* 370 NSLog(@"glyphRange : %@", NSStringFromRange(glyphRange)); 371 NSLog(@"characterRange: %@", NSStringFromRange(characterRange)); 372 */ 373 374 return NSMakeRange(startLine, endLine - startLine); 375} 376 377/* 378- (void) highlightLinesInRange: (NSRange)aRange 379{ 380 unsigned end; 381 unsigned len = [string length]; 382 383 end = -1; 384 for (i = 0; i < aRange.start; i++) 385 { 386 [string getLineStart: nil 387 end: nil 388 contentsEnd: &end 389 forRange: NSMakeRange(end + 1, 0)]; 390 } 391} 392*/ 393 394- (void) drawRect: (NSRect) aRect 395{ 396 NSRange glyphRange, characterRange; 397 NSLayoutManager *lm = [self layoutManager]; 398 NSTextContainer *tc = [self textContainer]; 399 400 [[NSColor whiteColor] set]; 401 NSRectFill(aRect); 402 403 glyphRange = [lm glyphRangeForBoundingRect:aRect 404 inTextContainer: tc]; 405 406 if (!NSEqualRanges(glyphRange, NSMakeRange(0, 0))) 407 { 408 int i = 0; 409 int count = [blockCharacterRangesArray count]; 410 int charStart, charEnd; 411 412 characterRange = [lm characterRangeForGlyphRange: glyphRange 413 actualGlyphRange: NULL]; 414 415 for (i = 0; i < count; i += 2) 416 { 417 charStart = [[blockCharacterRangesArray objectAtIndex: i] 418 intValue]; 419 charEnd = [[blockCharacterRangesArray objectAtIndex: i+1] 420 intValue]; 421 if (charEnd >= characterRange.location) 422 { 423 break; 424 } 425 } 426 427 for (; i < count; i += 2) 428 { 429 charStart = [[blockCharacterRangesArray objectAtIndex: i] 430 intValue]; 431 charEnd = [[blockCharacterRangesArray objectAtIndex: i+1] 432 intValue]; 433 if (charStart > NSMaxRange(characterRange)) 434 { 435 break; 436 } 437 438 blockRectsArray[i/2].size.width = aRect.size.width; 439 blockRectsArray[i/2].origin.x = aRect.origin.x; 440 441 [[colors objectAtIndex: i/2] set]; 442 443 NSRectFill(NSIntersectionRect(aRect, blockRectsArray[i/2])); 444 445 } 446 447 } 448 449 [super drawRect: aRect]; 450} 451 452- (void) superviewFrameChanged: (NSNotification *)aNotification 453{ 454 NSSize size; 455 float superWidth, selfWidth; 456 457 superWidth = [self convertRect: [_super_view bounds] 458 fromView: _super_view].size.width; 459 selfWidth = [_layoutManager 460 usedRectForTextContainer: _textContainer].size.width; 461 462 if (superWidth > selfWidth) 463 size.width = superWidth; 464 else 465 size.width = selfWidth; 466 467 size.height = [self frame].size.height; 468 469 [self setFrameSize: size]; 470} 471 472- (void) scrollWheel: (NSEvent *)theEvent 473{ 474 if ([self nextResponder]) 475 return [[self nextResponder] scrollWheel: theEvent]; 476 else 477 return [self noResponderFor: @selector(scrollWheel:)]; 478} 479@end 480 481 482