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