root/trunk/Views/JVMarkedScroller.m

Revision 3460, 16.9 kB (checked in by timothy, 2 years ago)

Removing the retain/autorelease idiom for accessor methods.

Line 
1 #import "JVMarkedScroller.h"
2
3 struct _mark {
4         unsigned long long location;
5         NSString *identifier;
6         NSColor *color;
7 };
8
9 @implementation JVMarkedScroller
10 - (id) initWithFrame:(NSRect) frame {
11         if( ( self = [super initWithFrame:frame] ) ) {
12                 _marks = [[NSMutableSet set] retain];
13                 _shades = [[NSMutableArray array] retain];
14                 _nearestPreviousMark = NSNotFound;
15                 _nearestNextMark = NSNotFound;
16                 _currentMark = NSNotFound;
17         }
18         return self;
19 }
20
21 - (void) dealloc {
22         [_marks release];
23         [_shades release];
24
25         _marks = nil;
26         _shades = nil;
27
28         [super dealloc];
29 }
30
31 #pragma mark -
32
33 - (void) drawRect:(NSRect) rect {
34         [super drawRect:rect];
35
36         NSAffineTransform *transform = [NSAffineTransform transform];
37         float width = [[self class] scrollerWidthForControlSize:[self controlSize]];
38
39         float scale = [self scaleToContentView];
40         [transform scaleXBy:( sFlags.isHoriz ? scale : 1. ) yBy:( sFlags.isHoriz ? 1. : scale )];
41
42         float offset = [self rectForPart:NSScrollerKnobSlot].origin.y;
43         [transform translateXBy:( sFlags.isHoriz ? offset / scale : 0. ) yBy:( sFlags.isHoriz ? 0. : offset / scale )];
44
45         NSBezierPath *shades = [NSBezierPath bezierPath];
46         NSEnumerator *enumerator = [_shades objectEnumerator];
47         NSNumber *startNum = nil;
48         NSNumber *stopNum = nil;
49
50         while( ( startNum = [enumerator nextObject] ) && ( stopNum = [enumerator nextObject] ) ) {
51                 unsigned long long start = [startNum unsignedLongLongValue];
52                 unsigned long long stop = [stopNum unsignedLongLongValue];
53
54                 NSRect rect = NSZeroRect;
55                 if( sFlags.isHoriz ) rect = NSMakeRect( start, 0., ( stop - start ), width );
56                 else rect = NSMakeRect( 0., start, width, ( stop - start ) );
57
58                 rect.origin = [transform transformPoint:rect.origin];
59                 rect.size = [transform transformSize:rect.size];
60
61                 [shades appendBezierPathWithRect:rect];
62         }
63
64         if( ( [_shades count] % 2 ) == 1 ) {
65                 NSRect rect = NSZeroRect;
66                 unsigned long long start = [[_shades lastObject] unsignedLongLongValue];
67                 unsigned long long stop = [self contentViewLength];
68
69                 if( sFlags.isHoriz ) rect = NSMakeRect( start, 0., ( stop - start ), width );
70                 else rect = NSMakeRect( 0., start, width, ( stop - start ) );
71
72                 rect.origin = [transform transformPoint:rect.origin];
73                 rect.size = [transform transformSize:rect.size];
74
75                 [shades appendBezierPathWithRect:NSIntegralRect( rect )];
76         }
77
78         NSRectClip( NSInsetRect( [self rectForPart:NSScrollerKnobSlot], ( sFlags.isHoriz ? 4. : 3. ), ( sFlags.isHoriz ? 3. : 4. ) ) );
79
80         if( ! [shades isEmpty ] ) {
81                 [[[NSColor knobColor] colorWithAlphaComponent:0.45] set];
82                 [shades fill];
83         }
84
85         NSBezierPath *lines = [NSBezierPath bezierPath];
86         NSMutableArray *lineArray = [NSMutableArray array];
87         enumerator = [_marks objectEnumerator];
88         NSValue *currentMark = nil;
89
90         unsigned long long currentPosition = ( _currentMark != NSNotFound ? _currentMark : [self floatValue] * [self contentViewLength] );
91         BOOL foundNext = NO, foundPrevious = NO;
92         NSRect knobRect = [self rectForPart:NSScrollerKnob];
93
94         while( ( currentMark = [enumerator nextObject] ) ) {
95                 struct _mark mark;
96                 [currentMark getValue:&mark];
97                 unsigned long long value = mark.location;
98
99                 if( value < currentPosition && ( ! foundPrevious || value > _nearestPreviousMark ) ) {
100                         _nearestPreviousMark = value;
101                         foundPrevious = YES;
102                 }
103
104                 if( value > currentPosition && ( ! foundNext || value < _nearestNextMark ) ) {
105                         _nearestNextMark = value;
106                         foundNext = YES;
107                 }
108
109                 NSPoint point = NSMakePoint( ( sFlags.isHoriz ? value : 0. ), ( sFlags.isHoriz ? 0. : value ) );
110                 point = [transform transformPoint:point];
111                 point.x = ( sFlags.isHoriz ? roundf( point.x ) + 0.5 : point.x );
112                 point.y = ( sFlags.isHoriz ? point.y : roundf( point.y ) + 0.5 );
113
114                 if( ! NSPointInRect( point, knobRect ) ) {
115                         if( mark.color ) {
116                                 NSBezierPath *line = [NSBezierPath bezierPath];
117                                 [line moveToPoint:point];
118
119                                 point = NSMakePoint( ( sFlags.isHoriz ? 0. : width ), ( sFlags.isHoriz ? width : 0. ) );
120                                 [line relativeLineToPoint:point];
121                                 [lineArray addObject:mark.color];
122                                 [lineArray addObject:line];
123                         } else {
124                                 [lines moveToPoint:point];
125
126                                 point = NSMakePoint( ( sFlags.isHoriz ? 0. : width ), ( sFlags.isHoriz ? width : 0. ) );
127                                 [lines relativeLineToPoint:point];
128                         }
129                 }
130         }
131
132         if( ! foundPrevious ) _nearestPreviousMark = NSNotFound;
133         if( ! foundNext ) _nearestNextMark = NSNotFound;
134
135         if( ! [lines isEmpty] ) {
136                 [[NSColor selectedKnobColor] set];
137                 [lines stroke];
138         }
139
140         // This is so we can draw the colored lines after the regular lines
141         enumerator = [lineArray objectEnumerator];
142         NSColor *lineColor = nil;
143         while( ( lineColor = [enumerator nextObject] ) ) {
144                 [lineColor set];
145                 [[enumerator nextObject] stroke];
146         }
147
148         if( ! [shades isEmpty] )
149                 [self drawKnob];
150 }
151
152 - (void) setFloatValue:(float) position knobProportion:(float) percent {
153         if( ! _jumpingToMark ) _currentMark = NSNotFound;
154         if( ( [self floatValue] != position || [self knobProportion] != percent ) && ( [_marks count] || [_shades count] ) )
155                 [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
156         [super setFloatValue:position knobProportion:percent];
157 }
158
159 - (NSMenu *) menuForEvent:(NSEvent *) event {
160         NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
161         NSMenuItem *item = nil;
162
163         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Clear All Marks", "clear all marks contextual menu item title" ) action:@selector( removeAllMarks ) keyEquivalent:@""] autorelease];
164         [item setTarget:self];
165         [menu addItem:item];
166
167         if( sFlags.isHoriz ) {
168                 item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Clear Marks from Here Left", "clear marks from here left contextual menu") action:@selector( clearMarksHereLess: ) keyEquivalent:@""] autorelease];
169                 [item setTarget:self];
170                 [menu addItem:item];
171
172                 item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Clear Marks from Here Right", "clear marks from here right contextual menu") action:@selector( clearMarksHereGreater: ) keyEquivalent:@""] autorelease];
173                 [item setTarget:self];
174                 [menu addItem:item];
175         } else {
176                 item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Clear Marks from Here Up", "clear marks from here up contextual menu") action:@selector( clearMarksHereLess: ) keyEquivalent:@""] autorelease];
177                 [item setTarget:self];
178                 [menu addItem:item];
179
180                 item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Clear Marks from Here Down", "clear marks from here up contextual menu") action:@selector( clearMarksHereGreater: ) keyEquivalent:@""] autorelease];
181                 [item setTarget:self];
182                 [menu addItem:item];
183         }
184
185         [menu addItem:[NSMenuItem separatorItem]];
186
187         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Jump to Previous Mark", "jump to previous mark contextual menu") action:@selector( jumpToPreviousMark: ) keyEquivalent:@"["] autorelease];
188         [item setTarget:self];
189         [menu addItem:item];
190
191         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Jump to Next Mark", "jump to next mark contextual menu") action:@selector( jumpToNextMark: ) keyEquivalent:@"]"] autorelease];
192         [item setTarget:self];
193         [menu addItem:item];
194
195         return menu;
196 }
197
198 #pragma mark -
199
200 - (void) updateNextAndPreviousMarks {
201         NSEnumerator *enumerator = [_marks objectEnumerator];
202         NSValue *currentMark = nil;
203
204         unsigned long long currentPosition = ( _currentMark != NSNotFound ? _currentMark : [self floatValue] * [self contentViewLength] );
205         BOOL foundNext = NO, foundPrevious = NO;
206
207         while( ( currentMark = [enumerator nextObject] ) ) {
208                 struct _mark mark;
209                 [currentMark getValue:&mark];
210                 unsigned long long value = mark.location;
211
212                 if( value < currentPosition && ( ! foundPrevious || value > _nearestPreviousMark ) ) {
213                         _nearestPreviousMark = value;
214                         foundPrevious = YES;
215                 }
216
217                 if( value > currentPosition && ( ! foundNext || value < _nearestNextMark ) ) {
218                         _nearestNextMark = value;
219                         foundNext = YES;
220                 }
221         }
222
223         if( ! foundPrevious ) _nearestPreviousMark = NSNotFound;
224         if( ! foundNext ) _nearestNextMark = NSNotFound;
225 }
226
227 #pragma mark -
228
229 - (IBAction) clearMarksHereLess:(id) sender {
230         NSEvent *event = [[NSApplication sharedApplication] currentEvent];
231         NSPoint where = [self convertPoint:[event locationInWindow] fromView:nil];
232         NSRect slotRect = [self rectForPart:NSScrollerKnobSlot];
233         float scale = [self scaleToContentView];
234         [self removeMarksLessThan:( ( sFlags.isHoriz ? where.x - NSMinX( slotRect ) : where.y - NSMinY( slotRect ) ) / scale )];
235 }
236
237 - (IBAction) clearMarksHereGreater:(id) sender {
238         NSEvent *event = [[NSApplication sharedApplication] currentEvent];
239         NSPoint where = [self convertPoint:[event locationInWindow] fromView:nil];
240         NSRect slotRect = [self rectForPart:NSScrollerKnobSlot];
241         float scale = [self scaleToContentView];
242         [self removeMarksGreaterThan:( ( sFlags.isHoriz ? where.x - NSMinX( slotRect ) : where.y - NSMinY( slotRect ) ) / scale )];
243 }
244
245 #pragma mark -
246
247 - (void) setLocationOfCurrentMark:(unsigned long long) location {
248         if( _currentMark != location ) {
249                 _currentMark = location;
250                 [self updateNextAndPreviousMarks];
251         }
252 }
253
254 - (unsigned long long) locationOfCurrentMark {
255         return _currentMark;
256 }
257
258 #pragma mark -
259
260 - (IBAction) jumpToPreviousMark:(id) sender {
261         if( _nearestPreviousMark != NSNotFound ) {
262                 _currentMark = _nearestPreviousMark;
263                 _jumpingToMark = YES;
264                 float shift = [self shiftAmountToCenterAlign];
265                 [[(NSScrollView *)[self superview] documentView] scrollPoint:NSMakePoint( 0., _currentMark - shift )];
266                 _jumpingToMark = NO;
267         }
268 }
269
270 - (IBAction) jumpToNextMark:(id) sender {
271         if( _nearestNextMark != NSNotFound ) {
272                 _currentMark = _nearestNextMark;
273                 _jumpingToMark = YES;
274                 float shift = [self shiftAmountToCenterAlign];
275                 [[(NSScrollView *)[self superview] documentView] scrollPoint:NSMakePoint( 0., _currentMark - shift )];
276                 _jumpingToMark = NO;
277         }
278 }
279
280 - (void) jumpToMarkWithIdentifier:(NSString *) identifier {
281         _jumpingToMark = YES;
282
283         NSEnumerator *e = [_marks objectEnumerator];
284         NSValue *obj = nil;
285         BOOL foundMark = NO;
286
287         while( ( obj = [e nextObject] ) ) {
288                 struct _mark mark;
289                 [obj getValue:&mark];
290                 if( [mark.identifier isEqualToString:identifier] ) {
291                         _currentMark = mark.location;
292                         foundMark = YES;
293                         break;
294                 }
295         }
296        
297         if( foundMark ) {
298                 float shift = [self shiftAmountToCenterAlign];
299                 [[(NSScrollView *)[self superview] documentView] scrollPoint:NSMakePoint( 0., _currentMark - shift )];         
300         }
301
302         _jumpingToMark = NO;
303 }
304
305 #pragma mark -
306
307 - (void) shiftMarksAndShadedAreasBy:(long long) displacement {
308         BOOL negative = ( displacement >= 0 ? NO : YES );
309         NSMutableSet *shiftedMarks = [NSMutableSet set];
310         NSValue *location = nil;
311
312         if( ! ( negative && _nearestPreviousMark < ABS( displacement ) ) ) _nearestPreviousMark += displacement;
313         else _nearestPreviousMark = NSNotFound;
314
315         if( ! ( negative && _nearestNextMark < ABS( displacement ) ) ) _nearestNextMark += displacement;
316         else _nearestNextMark = NSNotFound;
317
318         if( ! ( negative && _currentMark < ABS( displacement ) ) ) _currentMark += displacement;
319         else _currentMark = NSNotFound;
320
321         NSEnumerator *enumerator = [_marks objectEnumerator];
322         while( ( location = [enumerator nextObject] ) ) {
323                 struct _mark mark;
324                 [location getValue:&mark];
325                 if( ! ( negative && mark.location < ABS( displacement ) ) ) {
326                         mark.location += displacement;
327                         [shiftedMarks addObject:[NSValue value:&mark withObjCType:@encode( struct _mark )]];
328                 }
329         }
330
331         [_marks setSet:shiftedMarks];
332
333         NSMutableArray *shiftedShades = [NSMutableArray array];
334         NSNumber *start = nil;
335         NSNumber *stop = nil;
336
337         enumerator = [_shades objectEnumerator];
338         while( ( start = [enumerator nextObject] ) && ( ( stop = [enumerator nextObject] ) || YES ) ) {
339                 unsigned long long shiftedStart = [start unsignedLongLongValue];
340
341                 if( stop ) {
342                         unsigned long long shiftedStop = [stop unsignedLongLongValue];
343                         if( ! ( negative && shiftedStart < ABS( displacement ) ) && ! ( negative && shiftedStop < ABS( displacement ) ) ) {
344                                 [shiftedShades addObject:[NSNumber numberWithUnsignedLongLong:( shiftedStart + displacement )]];
345                                 [shiftedShades addObject:[NSNumber numberWithUnsignedLongLong:( shiftedStop + displacement )]];
346                         }
347                 } else if( ! ( negative && shiftedStart < ABS( displacement ) ) ) {
348                         [shiftedShades addObject:[NSNumber numberWithUnsignedLongLong:( shiftedStart + displacement )]];
349                 }
350         }
351
352         [_shades setArray:shiftedShades];
353
354         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
355 }
356
357 #pragma mark -
358
359 - (void) addMarkAt:(unsigned long long) location {
360         [self addMarkAt:location withIdentifier:nil withColor:nil];
361 }
362
363 - (void) addMarkAt:(unsigned long long) location withIdentifier:(NSString *) identifier {
364         [self addMarkAt:location withIdentifier:identifier withColor:nil];
365 }
366
367 - (void) addMarkAt:(unsigned long long) location withColor:(NSColor *) color {
368         [self addMarkAt:location withIdentifier:nil withColor:color];
369 }
370
371 - (void) addMarkAt:(unsigned long long) location withIdentifier:(NSString *) identifier withColor:(NSColor *) color {
372         struct _mark mark = {location, identifier, color};
373         [_marks addObject:[NSValue value:&mark withObjCType:@encode( struct _mark )]];
374         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
375 }
376
377 - (void) removeMarkAt:(unsigned long long) location {
378         [self removeMarkAt:location withIdentifier:nil withColor:nil];
379 }
380
381 - (void) removeMarkAt:(unsigned long long) location withIdentifier:(NSString *) identifier {
382         [self removeMarkAt:location withIdentifier:identifier withColor:nil];
383 }
384
385 - (void) removeMarkAt:(unsigned long long) location withColor:(NSColor *) color {
386         [self removeMarkAt:location withIdentifier:nil withColor:color];
387 }
388
389 - (void) removeMarkAt:(unsigned long long) location withIdentifier:(NSString *) identifier withColor:(NSColor *) color {
390         struct _mark mark = {location, identifier, color};
391         [_marks removeObject:[NSValue value:&mark withObjCType:@encode( struct _mark )]];
392         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
393 }
394
395 - (void) removeMarkWithIdentifier:(NSString *) identifier {
396         NSEnumerator *e = [[[_marks copy] autorelease] objectEnumerator];
397         NSValue *obj = nil;
398         while( ( obj = [e nextObject] ) ) {
399                 struct _mark mark;
400                 [obj getValue:&mark];
401                 if( [mark.identifier isEqualToString:identifier] ) {
402                         [_marks removeObject:obj];
403                 }
404         }
405
406         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
407 }
408
409 - (void) removeMarksGreaterThan:(unsigned long long) location {
410         NSEnumerator *enumerator = [[[_marks copy] autorelease] objectEnumerator];
411         NSValue *obj = nil;
412
413         while( ( obj = [enumerator nextObject] ) ) {
414                 struct _mark mark;
415                 [obj getValue:&mark];
416                 if( mark.location > location )
417                         [_marks removeObject:obj];
418         }
419
420         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
421 }
422
423 - (void) removeMarksLessThan:(unsigned long long) location {
424         NSEnumerator *enumerator = [[[_marks copy] autorelease] objectEnumerator];
425         NSValue *obj = nil;
426
427         while( ( obj = [enumerator nextObject] ) ) {
428                 struct _mark mark;
429                 [obj getValue:&mark];
430                 if( mark.location < location )
431                         [_marks removeObject:obj];
432         }
433
434         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
435 }
436
437 - (void) removeMarksInRange:(NSRange) range {
438         NSEnumerator *enumerator = [[[_marks copy] autorelease] objectEnumerator];
439         NSValue *obj = nil;
440
441         while( ( obj = [enumerator nextObject] ) ) {
442                 struct _mark mark;
443                 [obj getValue:&mark];
444                 if( NSLocationInRange( (unsigned int)mark.location, range ) )
445                         [_marks removeObject:obj];
446         }
447
448         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
449 }
450
451 - (void) removeAllMarks {
452         [_marks removeAllObjects];
453         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
454 }
455
456 #pragma mark -
457
458 - (void) setMarks:(NSSet *) marks {
459         [_marks setSet:marks];
460         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
461 }
462
463 - (NSSet *) marks {
464         return _marks;
465 }
466
467 #pragma mark -
468
469 - (void) startShadedAreaAt:(unsigned long long) location {
470         if( ! [_shades count] || ! ( [_shades count] % 2 ) ) {
471                 [_shades addObject:[NSNumber numberWithUnsignedLongLong:location]];
472                 [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
473         }
474 }
475
476 - (void) stopShadedAreaAt:(unsigned long long) location {
477         if( [_shades count] && ( [_shades count] % 2 ) == 1 ) {
478                 [_shades addObject:[NSNumber numberWithUnsignedLongLong:location]];
479                 [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
480         }
481 }
482
483 #pragma mark -
484
485 - (void) removeAllShadedAreas {
486         [_shades removeAllObjects];
487         [self setNeedsDisplayInRect:[self rectForPart:NSScrollerKnobSlot]];
488 }
489
490 #pragma mark -
491
492 - (unsigned long long) contentViewLength {
493         if( sFlags.isHoriz ) return ( NSWidth( [self frame] ) / [self knobProportion] );
494         else return ( NSHeight( [self frame] ) / [self knobProportion] );
495 }
496
497 - (float) scaleToContentView {
498         if( sFlags.isHoriz ) return NSWidth( [self rectForPart:NSScrollerKnobSlot] ) / NSWidth( [[(NSScrollView *)[self superview] contentView] documentRect] );
499         else return NSHeight( [self rectForPart:NSScrollerKnobSlot] ) / NSHeight( [[(NSScrollView *)[self superview] contentView] documentRect] );
500 }
501
502 - (float) shiftAmountToCenterAlign {
503         float scale = [self scaleToContentView];
504         if( sFlags.isHoriz ) return ( ( NSWidth( [self rectForPart:NSScrollerKnobSlot] ) * [self knobProportion] ) / 2. ) / scale;
505         else return ( ( NSHeight( [self rectForPart:NSScrollerKnobSlot] ) * [self knobProportion] ) / 2. ) / scale;
506 }
507 @end
Note: See TracBrowser for help on using the browser.