1/* 2 * DiffWindowController.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#import <AppKit/AppKit.h> 25//#include "DiffTextView.h" 26//#include "DiffMiddleView.h" 27#import "DiffView.h" 28#import "DiffWindowController.h" 29#import "DiffWrapper.h" 30 31//extern void tasktest(NSString *file1, NSString *file2, NSMutableArray **r1, NSMutableArray **r2); 32 33 34@interface PatchData : NSObject 35{ 36@public 37 NSMutableString *patchString; 38 int start; 39 int end; 40 int lineDifference; 41} 42@end 43 44@implementation PatchData 45@end 46 47 48 49 50@interface DiffWindowController (Private) 51- (void) diffViewFrameDidChange: (NSNotification*) aNotification; 52- (id) _initWithFilename: (NSString *) filename1 53 andFilename: (NSString *) filename2; 54- (NSMutableString *) _patchMutableString: (int) direction; 55- (void) saveMergedAt: (NSString *) mergedName; 56@end 57 58@implementation DiffWindowController 59 60 61- (id) initWithFilename: (NSString *) filename1 62 andTempFilename: (NSString *) filename2 63{ 64 /* Switch filenames so the CVS version is on the left */ 65 [self _initWithFilename: filename2 66 andFilename: filename1]; 67 68 tempFilename = filename2; 69 70 [[self window] setTitle: 71 [NSString 72 stringWithFormat: 73 @"Comparing %@ to the CVS version", 74 [filename1 lastPathComponent]]]; 75 76 [[self window] makeKeyAndOrderFront: self]; 77 78 leftFileName = nil; 79 rightFileName = RETAIN(filename1); 80 81 return self; 82} 83 84- (id) initWithFilename: (NSString *) filename1 85 andFilename: (NSString *) filename2 86{ 87 [self _initWithFilename: filename1 88 andFilename: filename2]; 89 90 tempFilename = nil; 91 92 [[self window] setTitle: 93 [NSString 94 stringWithFormat: 95 @"Comparing %@ to %@", 96 [filename1 lastPathComponent], 97 [filename2 lastPathComponent]]]; 98 99 100 [[self window] makeKeyAndOrderFront: self]; 101 102 /* 103 [NSApp addWindowsItem: [self window] 104 title: [[self window] title] 105 filename: NO]; 106 */ 107 108 leftFileName = RETAIN(filename1); 109 rightFileName = RETAIN(filename2); 110 111 NSDebugLog(@"windowsMenu %@", [NSApp windowsMenu]); 112 113 return self; 114} 115 116 117- (id) _initWithFilename: (NSString *) filename1 118 andFilename: (NSString *) filename2 119{ 120 [self initWithWindowNibName: @"window"]; 121 122 [self window]; 123 124 125 NSDebugLog(@"%@", scroller); 126 [scroller setArrowsPosition: NSScrollerArrowsMaxEnd]; 127 [scroller setFloatValue:0.5 knobProportion:0.2]; 128 [scroller setEnabled: YES]; 129 130 diffWrapper = [[DiffWrapper alloc] initWithFilename: filename1 131 andFilename: filename2]; 132 133 [diffWrapper compare]; 134 135 leftChanges = [diffWrapper leftChanges]; 136 rightChanges = [diffWrapper rightChanges]; 137 // tasktest(filename1, filename2, &leftChanges, &rightChanges); 138 139 RETAIN(leftChanges); 140 RETAIN(rightChanges); 141 142 { 143 NSString *string1; 144 NSString *string2; 145 146 string1 = [NSString stringWithContentsOfFile: 147 filename1]; 148 string2 = [NSString stringWithContentsOfFile: 149 filename2]; 150 151 152 [diffView setLeftString: string1]; 153 [diffView setRightString: string2]; 154 155 [diffView setLeftChanges: leftChanges 156 andRightChanges: rightChanges]; 157 158 [diffView setLeftLineRanges: [diffWrapper leftLineRanges] 159 andRightLineRanges: [diffWrapper rightLineRanges]]; 160 161 [scroller setFloatValue: 0. 162 knobProportion: 163 ([diffView frame].size.height / 164 [diffView mergeFileHeight])]; 165 166 [diffView tile]; 167 168 [diffView setPostsFrameChangedNotifications: YES]; 169 [[NSNotificationCenter defaultCenter] 170 addObserver: self 171 selector: @selector(diffViewFrameDidChange:) 172 name: NSViewFrameDidChangeNotification 173 object: diffView]; 174 175 choices = (int *) calloc([leftChanges count], sizeof(int)); 176 177 } 178 179 // NSLog(@"gonna orderFront %@",[self window]); 180 [self diffViewFrameDidChange: nil]; 181 // [[self window] orderFrontRegardless]; 182 183 return self; 184 185} 186 187- (void) diffViewFrameDidChange: (NSNotification*) aNotification 188{ 189 190 [diffView computeScrollerSize]; 191 192 if ([diffView frame].size.height >= [diffView mergeFileHeight]) 193 { 194 [scroller setEnabled: NO]; 195 } 196 else 197 { 198 [scroller setEnabled: YES]; 199 [scroller setFloatValue: [scroller floatValue] 200 knobProportion: 201 ([diffView frame].size.height / 202 [diffView mergeFileHeight])]; 203 [diffView doScroll: scroller]; 204 } 205} 206 207- (void) windowWillClose: (id) sender 208{ 209 NSDebugLog(@"windowWillClose called"); 210 // RELEASE([self window]); 211 AUTORELEASE(self); 212} 213 214- (void) dealloc 215{ 216 NSDebugLog(@"dealloc called"); 217 if (tempFilename != nil) 218 { 219 if ([[NSFileManager defaultManager] 220 removeFileAtPath: tempFilename 221 handler: nil] == NO) 222 NSLog(@"We could not delete %@", tempFilename); 223 } 224 TEST_RELEASE(leftFileName); 225 TEST_RELEASE(rightFileName); 226 [super dealloc]; 227} 228 229 230- (void) matrixButtonClicked: (id) sender 231{ 232 int oldChoice = choices[[sender tag]]; 233 234 NSDebugLog(@"matrixButtonClicked %d, %d", [sender tag], 235 [sender selectedColumn]); 236 237 238 choices[[sender tag]] = [sender selectedColumn] - 1; 239 240 if ((oldChoice == -1) && (choices[[sender tag]] != -1)) 241 { 242 [[diffView leftTextView] setColor: 243 [NSColor colorWithDeviceRed:0.7 244 green:0.7 245 blue:1. 246 alpha:1.] 247 forChangeNumber: [sender tag]]; 248 } 249 else if ((oldChoice != -1) && (choices[[sender tag]] == -1)) 250 { 251 [[diffView leftTextView] setColor: 252 [NSColor whiteColor] 253 forChangeNumber: [sender tag]]; 254 } 255 256 if ((oldChoice == 1) && (choices[[sender tag]] != 1)) 257 { 258 [[diffView rightTextView] setColor: 259 [NSColor colorWithDeviceRed:0.7 260 green:0.7 261 blue:1. 262 alpha:1.] 263 forChangeNumber: [sender tag]]; 264 } 265 else if ((oldChoice != 1) && (choices[[sender tag]] == 1)) 266 { 267 [[diffView rightTextView] setColor: 268 [NSColor whiteColor] 269 forChangeNumber: [sender tag]]; 270 } 271} 272 273- (void) selectAllLeftChanges: (id) sender 274{ 275 int i; 276 NSArray *matrixArray = [[diffView middleView] matrixArray]; 277 int count = [matrixArray count]; 278 279 for ( i = 0; i < count; i ++ ) 280 { 281 if (choices[i] != - 1) 282 { 283 choices[i] = -1; 284 [[diffView leftTextView] setColor: 285 [NSColor whiteColor] 286 forChangeNumber: i]; 287 [[diffView rightTextView] setColor: 288 [NSColor colorWithDeviceRed:0.7 289 green:0.7 290 blue:1. 291 alpha:1.] 292 forChangeNumber: i]; 293 [[matrixArray objectAtIndex: i] selectCellAtRow: 0 column:0]; 294 } 295 } 296} 297 298- (void) selectAllRightChanges: (id) sender 299{ 300 int i; 301 NSArray *matrixArray = [[diffView middleView] matrixArray]; 302 int count = [matrixArray count]; 303 304 for ( i = 0; i < count; i ++ ) 305 { 306 if (choices[i] != 1) 307 { 308 choices[i] = 1; 309 [[diffView rightTextView] setColor: 310 [NSColor whiteColor] 311 forChangeNumber: i]; 312 [[diffView leftTextView] setColor: 313 [NSColor colorWithDeviceRed:0.7 314 green:0.7 315 blue:1. 316 alpha:1.] 317 forChangeNumber: i]; 318 [[matrixArray objectAtIndex: i] selectCellAtRow: 0 column:2]; 319 } 320 } 321} 322 323 324- (void) selectNoChanges: (id) sender 325{ 326 int i; 327 NSArray *matrixArray = [[diffView middleView] matrixArray]; 328 int count = [matrixArray count]; 329 330 for ( i = 0; i < count; i ++ ) 331 { 332 if (choices[i] != 0) 333 { 334 choices[i] = 0; 335 [[diffView leftTextView] setColor: 336 [NSColor colorWithDeviceRed:0.7 337 green:0.7 338 blue:1. 339 alpha:1.] 340 forChangeNumber: i]; 341 [[diffView rightTextView] setColor: 342 [NSColor colorWithDeviceRed:0.7 343 green:0.7 344 blue:1. 345 alpha:1.] 346 forChangeNumber: i]; 347 [[matrixArray objectAtIndex: i] selectCellAtRow: 0 column:1]; 348 } 349 } 350 351} 352 353- (void) savePatchFromRight: (id) sender 354{ 355 NSMutableString *patchString; 356 357 int result; 358 NSSavePanel *sPanel; 359 NSString *path = [[NSUserDefaults standardUserDefaults] 360 objectForKey:@"OpenDirectory"]; 361 NSString *mergedName; 362 363 364 sPanel = [NSSavePanel savePanel]; 365 // [sPanel setAllowsMultipleSelection:NO]; 366 result = [sPanel runModalForDirectory: path 367 file: @""]; 368 369 if (result != NSOKButton) 370 return; 371 372 path = [sPanel directory]; 373 374 mergedName = [sPanel filename]; 375 376 [[NSUserDefaults standardUserDefaults] 377 setObject: path 378 forKey:@"OpenDirectory"]; 379 380 381 patchString = [self _patchMutableString: -1]; 382 [patchString insertString: [NSString stringWithFormat: @"+++ %@\n", 383 leftFileName] 384 atIndex: 0]; 385 if (rightFileName) 386 { 387 [patchString insertString: [NSString stringWithFormat: @"--- %@\n", 388 rightFileName] 389 atIndex: 0]; 390 } 391 else 392 { 393 [patchString insertString: [NSString stringWithFormat: @"--- %@\n", 394 leftFileName] 395 atIndex: 0]; 396 } 397 398 [patchString writeToFile: mergedName atomically: YES]; 399} 400 401- (void) savePatchFromLeft: (id) sender 402{ 403 NSMutableString *patchString; 404 405 int result; 406 NSSavePanel *sPanel; 407 NSString *path = [[NSUserDefaults standardUserDefaults] 408 objectForKey:@"OpenDirectory"]; 409 NSString *mergedName; 410 411 412 sPanel = [NSSavePanel savePanel]; 413 // [sPanel setAllowsMultipleSelection:NO]; 414 result = [sPanel runModalForDirectory: path 415 file: @""]; 416 417 if (result != NSOKButton) 418 return; 419 420 path = [sPanel directory]; 421 422 mergedName = [sPanel filename]; 423 424 [[NSUserDefaults standardUserDefaults] 425 setObject: path 426 forKey:@"OpenDirectory"]; 427 428 429 patchString = [self _patchMutableString: 1]; 430 if (rightFileName) 431 { 432 [patchString insertString: [NSString stringWithFormat: @"+++ %@\n", 433 rightFileName] 434 atIndex: 0]; 435 } 436 else 437 { 438 [patchString insertString: [NSString stringWithFormat: @"+++ %@\n", 439 leftFileName] 440 atIndex: 0]; 441 } 442 [patchString insertString: [NSString stringWithFormat: @"--- %@\n", 443 leftFileName] 444 atIndex: 0]; 445 446 447 [patchString writeToFile: mergedName atomically: YES]; 448} 449 450- (NSMutableString *) _patchMutableString: (int) direction 451{ 452 NSArray *flr; 453 NSArray *tlr; 454 NSString *fstr; 455 NSString *tstr; 456 457 NSMutableArray *patchArray = [[NSMutableArray alloc] 458 initWithCapacity: 459 ([leftChanges count] / 2) + 1]; 460 461 NSMutableArray *finalArray = [[NSMutableArray alloc] 462 initWithCapacity: 463 ([leftChanges count] / 2) + 1]; 464 NSArray *fromChanges; 465 NSArray *toChanges; 466 NSMutableString *stringToInsert; 467 PatchData *p, *pp; 468 int i, j, k; 469 int toStart; 470 int toEnd; 471 int count; 472 int linesCount; 473 474 if (direction > 0) 475 { 476 flr = [diffWrapper leftLineRanges]; 477 tlr = [diffWrapper rightLineRanges]; 478 fstr = [diffWrapper leftString]; 479 tstr = [diffWrapper rightString]; 480 fromChanges = leftChanges; 481 toChanges = rightChanges; 482 } 483 else 484 { 485 flr = [diffWrapper rightLineRanges]; 486 tlr = [diffWrapper leftLineRanges]; 487 fstr = [diffWrapper rightString]; 488 tstr = [diffWrapper leftString]; 489 fromChanges = rightChanges; 490 toChanges = leftChanges; 491 } 492 linesCount = [flr count] - 1; 493 494 for ( i = 0; i < [fromChanges count] - 1; i += 2 ) 495 { 496 p = [[PatchData alloc] init]; 497 p->patchString = (NSMutableString*)[NSMutableString string]; 498 499 if (choices[i/2] * direction > 0) 500 { 501 p->start = [[fromChanges objectAtIndex: i] 502 intValue]; 503 p->end = [[fromChanges objectAtIndex: i + 1] 504 intValue]; 505 toStart = [[toChanges objectAtIndex: i] 506 intValue]; 507 toEnd = [[toChanges objectAtIndex: i + 1] 508 intValue]; 509 p->lineDifference = (toEnd - toStart) - 510 (p->end - p->start); 511 512 for (j = p->start; j < p->end; j++ ) 513 { 514 [p->patchString appendString: @"-"]; 515 [p->patchString appendString:[fstr 516 substringWithRange: 517 NSMakeRange 518 ([[flr objectAtIndex: j] intValue] + 1, 519 [[flr objectAtIndex: j+1] intValue] - 520 [[flr objectAtIndex: j] intValue])]]; 521 } 522 for (j = toStart; j < toEnd; j++ ) 523 { 524 [p->patchString appendString: @"+"]; 525 [p->patchString appendString:[tstr 526 substringWithRange: 527 NSMakeRange 528 ([[tlr objectAtIndex: j] intValue] + 1, 529 [[tlr objectAtIndex: j+1] intValue] - 530 [[tlr objectAtIndex: j] intValue])]]; 531 } 532 [patchArray addObject: p]; 533 534 } 535 else if (choices[i/2] * direction < 0) 536 { 537 } 538 else if (choices[i/2] == 0) 539 { 540 p->start = [[fromChanges objectAtIndex: i] 541 intValue]; 542 p->end = [[fromChanges objectAtIndex: i + 1] 543 intValue]; 544 p->lineDifference = - (p->end - p->start); 545 546 for (j = p->start; j < p->end; j++ ) 547 { 548 [p->patchString appendString: @"-"]; 549 [p->patchString appendString:[fstr 550 substringWithRange: 551 NSMakeRange 552 ([[flr objectAtIndex: j] intValue] + 1, 553 [[flr objectAtIndex: j+1] intValue] - 554 [[flr objectAtIndex: j] intValue])]]; 555 } 556 557 if (p->lineDifference != 0) 558 [patchArray addObject: p]; 559 560 } 561 } 562 563 564 565 566 i = 0; 567 count = [patchArray count]; 568 569 pp = nil; 570 571 while( i < count ) 572 { 573 p = [patchArray objectAtIndex: i]; 574 575 576 if ((pp == nil) || (p->start - pp->end >= 6)) 577 { 578 // two separate chunks 579 if (pp != nil) 580 { 581 j = pp->end + 3; 582 if (j >= linesCount) 583 j = linesCount; 584 stringToInsert = (NSMutableString*)[NSMutableString string]; 585 for ( k = pp->end; k < j; k++ ) 586 { 587 [stringToInsert appendString: @" "]; 588 [stringToInsert appendString: [fstr 589 substringWithRange: 590 NSMakeRange 591 ([[flr objectAtIndex: k] intValue] + 1, 592 [[flr objectAtIndex: k+1] intValue] - 593 [[flr objectAtIndex: k] intValue])]]; 594 } 595 [pp->patchString appendString: stringToInsert]; 596 pp->end = j; 597 598 [finalArray addObject: pp]; 599 } 600 601 // new chunk 602 pp = [[PatchData alloc] init];; 603 604 j = p->start - 3; 605 if ( j < 0) 606 j = 0; 607 pp = [[PatchData alloc] init]; 608 pp->patchString = p->patchString; 609 stringToInsert = (NSMutableString*)[NSMutableString string]; 610 for ( k = j; k < p->start; k++ ) 611 { 612 [stringToInsert appendString: @" "]; 613 [stringToInsert appendString: [fstr 614 substringWithRange: 615 NSMakeRange 616 ([[flr objectAtIndex: k] intValue] + 1, 617 [[flr objectAtIndex: k+1] intValue] - 618 [[flr objectAtIndex: k] intValue])]]; 619 } 620 [pp->patchString insertString: stringToInsert 621 atIndex: 0]; 622 pp->start = j; 623 pp->end = p->end; 624 pp->lineDifference = p->lineDifference; 625 626 } 627 else 628 { 629 // merge the two chunks 630 stringToInsert = (NSMutableString*)[NSMutableString string]; 631 for ( k = pp->end; k < p->start; k++ ) 632 { 633 [stringToInsert appendString: @" "]; 634 [stringToInsert appendString: [fstr 635 substringWithRange: 636 NSMakeRange 637 ([[flr objectAtIndex: k] intValue] + 1, 638 [[flr objectAtIndex: k+1] intValue] - 639 [[flr objectAtIndex: k] intValue])]]; 640 } 641 642 [pp->patchString appendString: stringToInsert]; 643 [pp->patchString appendString: p->patchString]; 644 pp->end = p->end; 645 pp->lineDifference += p->lineDifference; 646 } 647 i++; 648 } 649 650 if (pp != nil) 651 { 652 j = pp->end + 3; 653 if (j >= linesCount) 654 j = linesCount; 655 stringToInsert = (NSMutableString*)[NSMutableString string]; 656 for ( k = pp->end; k < j; k++ ) 657 { 658 [stringToInsert appendString: @" "]; 659 [stringToInsert appendString: [fstr 660 substringWithRange: 661 NSMakeRange 662 ([[flr objectAtIndex: k] intValue] + 1, 663 [[flr objectAtIndex: k+1] intValue] - 664 [[flr objectAtIndex: k] intValue])]]; 665 } 666 [pp->patchString appendString: stringToInsert]; 667 pp->end = j; 668 669 [finalArray addObject: pp]; 670 } 671 672 673 { 674 NSMutableString *patchFileString = 675 (NSMutableString*)[NSMutableString string]; 676 677 int d = 0; 678 for ( i = 0; i < [finalArray count]; i++ ) 679 { 680 p = [finalArray objectAtIndex: i]; 681 [patchFileString appendFormat: 682 @"@@ -%d,%d +%d,%d @@\n%@", 683 p->start+1, 684 p->end - p->start, 685 p->start + d + 1, 686 p->end - p->start + p->lineDifference, 687 p->patchString]; 688 d += p->lineDifference; 689 } 690 691 // NSLog(@"\n%@", patchFileString); 692 return patchFileString; 693 } 694 695} 696 697 698- (void) saveMergedAt: (NSString *) mergedName 699{ 700 NSMutableString *mergedString = [NSMutableString string]; 701 int i; 702 703 { 704 NSArray *llr = [diffWrapper leftLineRanges]; 705 NSArray *rlr = [diffWrapper rightLineRanges]; 706 707 [mergedString appendString: 708 [[diffWrapper leftString] 709 substringWithRange: 710 NSMakeRange 711 (0, 712 [[llr objectAtIndex: 713 [[leftChanges objectAtIndex: 0] 714 intValue]] intValue] 715 )]]; 716 717 718 for ( i = 0; i < [leftChanges count] - 1; i++ ) 719 { 720 if (i % 2 == 1) 721 { 722 [mergedString appendString: 723 [[diffWrapper leftString] 724 substringWithRange: 725 NSMakeRange 726 ([[llr objectAtIndex: 727 [[leftChanges objectAtIndex: i] 728 intValue]] intValue], 729 [[llr objectAtIndex: 730 [[leftChanges objectAtIndex: i+1] 731 intValue]] intValue] - 732 [[llr objectAtIndex: 733 [[leftChanges objectAtIndex: i] 734 intValue]] intValue] 735 )]]; 736 } 737 else 738 { 739 if (choices[i/2] < 0) 740 { 741 [mergedString appendString: 742 [[diffWrapper leftString] 743 substringWithRange: 744 NSMakeRange 745 ([[llr objectAtIndex: 746 [[leftChanges objectAtIndex: i] 747 intValue]] intValue], 748 [[llr objectAtIndex: 749 [[leftChanges objectAtIndex: i+1] 750 intValue]] intValue] - 751 [[llr objectAtIndex: 752 [[leftChanges objectAtIndex: i] 753 intValue]] intValue] 754 )]]; 755 756 } 757 else if (choices[i/2] > 0) 758 { 759 [mergedString appendString: 760 [[diffWrapper rightString] 761 substringWithRange: 762 NSMakeRange 763 ([[rlr objectAtIndex: 764 [[rightChanges objectAtIndex: i] 765 intValue]] intValue], 766 [[rlr objectAtIndex: 767 [[rightChanges objectAtIndex: i+1] 768 intValue]] intValue] - 769 [[rlr objectAtIndex: 770 [[rightChanges objectAtIndex: i] 771 intValue]] intValue] 772 )]]; 773 774 } 775 else 776 { 777 778 } 779 } 780 } 781 782 [mergedString appendString: 783 [[diffWrapper leftString] 784 substringFromIndex: 785 [[llr objectAtIndex: 786 [[leftChanges objectAtIndex: 787 [leftChanges count] - 1] 788 intValue]] intValue]]]; 789 790 // NSLog(@"%@", mergedString); 791 } 792 793 794 [mergedString writeToFile: mergedName atomically: YES]; 795} 796 797- (void) saveMergedFile: (id) sender 798{ 799 800 int result; 801 NSSavePanel *sPanel; 802 NSString *path = [[NSUserDefaults standardUserDefaults] 803 objectForKey:@"OpenDirectory"]; 804 NSString *mergedName; 805 806 807 sPanel = [NSSavePanel savePanel]; 808 // [sPanel setAllowsMultipleSelection:NO]; 809 result = [sPanel runModalForDirectory: path 810 file: @""]; 811 812 if (result != NSOKButton) 813 return; 814 815 path = [sPanel directory]; 816 817 mergedName = [sPanel filename]; 818 819 [[NSUserDefaults standardUserDefaults] 820 setObject: path 821 forKey:@"OpenDirectory"]; 822 823 824 // NSLog(@"%@", mergedName); 825 826 [self saveMergedAt: mergedName]; 827 828} 829@end 830