]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
macosx: Use input_ItemHasErrorWhenReading to display a small icon if there was an...
[vlc] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import <Cocoa/Cocoa.h>
26 #import <QuickTime/QuickTime.h>
27
28 #import "intf.h"                                          /* VLCApplication */
29 #import "misc.h"
30 #import "playlist.h"
31 #import "controls.h"
32
33 /*****************************************************************************
34  * NSImage (VLCAdditions)
35  *
36  *  Addition to NSImage
37  *****************************************************************************/
38 @implementation NSImage (VLCAdditions)
39 + (id)imageWithSystemName:(int)name
40 {
41     /* ugly Carbon stuff following...
42      * regrettably, you can't get the icons through clean Cocoa */
43
44     /* retrieve our error icon */
45     NSImage * icon;
46     IconRef ourIconRef;
47     int returnValue;
48     returnValue = GetIconRef(kOnSystemDisk, 'macs', name, &ourIconRef);
49     icon = [[[NSImage alloc] initWithSize:NSMakeSize(32,32)] autorelease];
50     [icon lockFocus];
51     CGRect rect = CGRectMake(0,0,32,32);
52     PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
53         graphicsPort],
54         &rect,
55         kAlignNone,
56         kTransformNone,
57         NULL /*inLabelColor*/,
58         kPlotIconRefNormalFlags,
59         (IconRef)ourIconRef);
60     [icon unlockFocus];
61     returnValue = ReleaseIconRef(ourIconRef);
62     return icon;
63 }
64
65 + (id)imageWithWarningIcon
66 {
67     static NSImage * imageWithWarningIcon = nil;
68     if( !imageWithWarningIcon )
69     {
70         imageWithWarningIcon = [[[self class] imageWithSystemName:'caut'] retain];
71     }
72     return imageWithWarningIcon;
73 }
74
75 + (id)imageWithErrorIcon
76 {
77     static NSImage * imageWithErrorIcon = nil;
78     if( !imageWithErrorIcon )
79     {
80         imageWithErrorIcon = [[[self class] imageWithSystemName:'stop'] retain];
81     }
82     return imageWithErrorIcon;
83 }
84
85 @end
86 /*****************************************************************************
87  * NSAnimation (VLCAdditions)
88  *
89  *  Missing extension to NSAnimation
90  *****************************************************************************/
91
92 @implementation NSAnimation (VLCAdditions)
93 /* fake class attributes  */
94 static NSMapTable *VLCAdditions_userInfo = NULL;
95
96 + (void)load
97 {
98     /* init our fake object attribute */
99     VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
100 }
101
102 - (void)dealloc
103 {
104     NSMapRemove(VLCAdditions_userInfo, self);
105     [super dealloc];
106 }
107
108 - (void)setUserInfo: (void *)userInfo
109 {
110     NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
111 }
112
113 - (void *)userInfo
114 {
115     return NSMapGet(VLCAdditions_userInfo, self);
116 }
117 @end
118
119 /*****************************************************************************
120  * NSScreen (VLCAdditions)
121  *
122  *  Missing extension to NSScreen
123  *****************************************************************************/
124
125 @implementation NSScreen (VLCAdditions)
126
127 static NSMutableArray *blackoutWindows = NULL;
128
129 + (void)load
130 {
131     /* init our fake object attribute */
132     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
133 }
134
135 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
136 {
137     int i;
138  
139     for( i = 0; i < [[NSScreen screens] count]; i++ )
140     {
141         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
142         if([screen displayID] == displayID)
143             return screen;
144     }
145     return nil;
146 }
147
148 - (BOOL)isMainScreen
149 {
150     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
151 }
152
153 - (BOOL)isScreen: (NSScreen*)screen
154 {
155     return ([self displayID] == [screen displayID]);
156 }
157
158 - (CGDirectDisplayID)displayID
159 {
160     return (CGDirectDisplayID)_screenNumber;
161 }
162
163 - (void)blackoutOtherScreens
164 {
165     unsigned int i;
166
167     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
168     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
169     [blackoutWindows removeAllObjects];
170
171     for(i = 0; i < [[NSScreen screens] count]; i++)
172     {
173         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
174         VLCWindow *blackoutWindow;
175         NSRect screen_rect;
176  
177         if([self isScreen: screen])
178             continue;
179
180         screen_rect = [screen frame];
181         screen_rect.origin.x = screen_rect.origin.y = 0;
182
183         /* blackoutWindow alloc strategy
184             - The NSMutableArray blackoutWindows has the blackoutWindow references
185             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
186         */
187         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
188                 backing: NSBackingStoreBuffered defer: NO screen: screen];
189         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
190         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
191  
192         [blackoutWindow displayIfNeeded];
193         [blackoutWindow orderFront: self animate: YES];
194
195         [blackoutWindows addObject: blackoutWindow];
196         [blackoutWindow release];
197         
198         if( [screen isMainScreen ] )
199            SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
200     }
201 }
202
203 + (void)unblackoutScreens
204 {
205     unsigned int i;
206
207     for(i = 0; i < [blackoutWindows count]; i++)
208     {
209         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
210         [blackoutWindow closeAndAnimate: YES];
211     }
212     
213    SetSystemUIMode( kUIModeNormal, 0);
214 }
215
216 @end
217
218 /*****************************************************************************
219  * VLCWindow
220  *
221  *  Missing extension to NSWindow
222  *****************************************************************************/
223
224 @implementation VLCWindow
225 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
226     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
227 {
228     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
229     if( self )
230         b_isset_canBecomeKeyWindow = NO;
231     return self;
232 }
233 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
234 {
235     b_isset_canBecomeKeyWindow = YES;
236     b_canBecomeKeyWindow = canBecomeKey;
237 }
238
239 - (BOOL)canBecomeKeyWindow
240 {
241     if(b_isset_canBecomeKeyWindow)
242         return b_canBecomeKeyWindow;
243
244     return [super canBecomeKeyWindow];
245 }
246
247 - (void)closeAndAnimate: (BOOL)animate
248 {
249     NSInvocation *invoc;
250  
251     if (!animate || MACOS_VERSION < 10.4f)
252     {
253         [super close];
254         return;
255     }
256
257     invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
258     [invoc setTarget: (id)super];
259
260     if (![self isVisible] || [self alphaValue] == 0.0)
261     {
262         [super close];
263         return;
264     }
265
266     [self orderOut: self animate: YES callback: invoc];
267 }
268
269 - (void)orderOut: (id)sender animate: (BOOL)animate
270 {
271     NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
272     [invoc setTarget: (id)super];
273     [invoc setArgument: sender atIndex: 0];
274     [self orderOut: sender animate: animate callback: invoc];
275 }
276
277 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
278 {
279     NSViewAnimation *anim;
280     NSViewAnimation *current_anim;
281     NSMutableDictionary *dict;
282
283     if (!animate || MACOS_VERSION < 10.4f)
284     {
285         [self orderOut: sender];
286         return;
287     }
288
289     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
290
291     [dict setObject:self forKey:NSViewAnimationTargetKey];
292
293     [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
294     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
295     [dict release];
296
297     [anim setAnimationBlockingMode:NSAnimationNonblocking];
298     [anim setDuration:0.9];
299     [anim setFrameRate:30];
300     [anim setUserInfo: callback];
301
302     @synchronized(self) {
303         current_anim = self->animation;
304
305         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
306         {
307             [anim release];
308         }
309         else
310         {
311             if (current_anim)
312             {
313                 [current_anim stopAnimation];
314                 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
315                 [current_anim release];
316             }
317             else
318                 [anim setCurrentProgress:1.0 - [self alphaValue]];
319             self->animation = anim;
320             [self setDelegate: self];
321             [anim startAnimation];
322         }
323     }
324 }
325
326 - (void)orderFront: (id)sender animate: (BOOL)animate
327 {
328     NSViewAnimation *anim;
329     NSViewAnimation *current_anim;
330     NSMutableDictionary *dict;
331  
332     if (!animate || MACOS_VERSION < 10.4f)
333     {
334         [super orderFront: sender];
335         [self setAlphaValue: 1.0];
336         return;
337     }
338
339     if (![self isVisible])
340     {
341         [self setAlphaValue: 0.0];
342         [super orderFront: sender];
343     }
344     else if ([self alphaValue] == 1.0)
345     {
346         [super orderFront: self];
347         return;
348     }
349
350     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
351
352     [dict setObject:self forKey:NSViewAnimationTargetKey];
353  
354     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
355     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
356     [dict release];
357  
358     [anim setAnimationBlockingMode:NSAnimationNonblocking];
359     [anim setDuration:0.5];
360     [anim setFrameRate:30];
361
362     @synchronized(self) {
363         current_anim = self->animation;
364
365         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
366         {
367             [anim release];
368         }
369         else
370         {
371             if (current_anim)
372             {
373                 [current_anim stopAnimation];
374                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
375                 [current_anim release];
376             }
377             else
378                 [anim setCurrentProgress:[self alphaValue]];
379             self->animation = anim;
380             [self setDelegate: self];
381             [self orderFront: sender];
382             [anim startAnimation];
383         }
384     }
385 }
386
387 - (void)animationDidEnd:(NSAnimation*)anim
388 {
389     if ([self alphaValue] <= 0.0)
390     {
391         NSInvocation * invoc;
392         [super orderOut: nil];
393         [self setAlphaValue: 1.0];
394         if ((invoc = [anim userInfo]))
395             [invoc invoke];
396     }
397 }
398 @end
399
400 /*****************************************************************************
401  * VLCControllerWindow
402  *****************************************************************************/
403
404 @implementation VLCControllerWindow
405
406 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
407     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
408 {
409     self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
410     backing:backingType defer:flag];
411
412     [[VLCMain sharedInstance] updateTogglePlaylistState];
413
414     return( self );
415 }
416
417 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
418 {
419     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
420        so we perform the menu equivalent now. */
421     if([[NSApp mainMenu] performKeyEquivalent:o_event])
422         return TRUE;
423
424     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
425            [(VLCControls *)[[VLCMain sharedInstance] getControls] keyEvent:o_event];
426 }
427
428 @end
429
430
431
432 /*****************************************************************************
433  * VLCControllerView
434  *****************************************************************************/
435
436 @implementation VLCControllerView
437
438 - (void)dealloc
439 {
440     [self unregisterDraggedTypes];
441     [super dealloc];
442 }
443
444 - (void)awakeFromNib
445 {
446     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
447         NSFilenamesPboardType, nil]];
448 }
449
450 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
451 {
452     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
453                 == NSDragOperationGeneric)
454     {
455         return NSDragOperationGeneric;
456     }
457     else
458     {
459         return NSDragOperationNone;
460     }
461 }
462
463 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
464 {
465     return YES;
466 }
467
468 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
469 {
470     NSPasteboard *o_paste = [sender draggingPasteboard];
471     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
472     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
473     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
474
475     if( o_carried_data )
476     {
477         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
478         {
479             int i;
480             NSArray *o_array = [NSArray array];
481             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
482                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
483
484             for( i = 0; i < (int)[o_values count]; i++)
485             {
486                 NSDictionary *o_dic;
487                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
488                 o_array = [o_array arrayByAddingObject: o_dic];
489             }
490             [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
491             return YES;
492         }
493     }
494     [self setNeedsDisplay:YES];
495     return YES;
496 }
497
498 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
499 {
500     [self setNeedsDisplay:YES];
501 }
502
503 @end
504
505 /*****************************************************************************
506  * VLBrushedMetalImageView
507  *****************************************************************************/
508
509 @implementation VLBrushedMetalImageView
510
511 - (BOOL)mouseDownCanMoveWindow
512 {
513     return YES;
514 }
515
516 - (void)dealloc
517 {
518     [self unregisterDraggedTypes];
519     [super dealloc];
520 }
521
522 - (void)awakeFromNib
523 {
524     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
525         NSFilenamesPboardType, nil]];
526 }
527
528 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
529 {
530     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
531                 == NSDragOperationGeneric)
532     {
533         return NSDragOperationGeneric;
534     }
535     else
536     {
537         return NSDragOperationNone;
538     }
539 }
540
541 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
542 {
543     return YES;
544 }
545
546 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
547 {
548     NSPasteboard *o_paste = [sender draggingPasteboard];
549     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
550     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
551     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
552     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
553
554     if( o_carried_data )
555     {
556         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
557         {
558             int i;
559             NSArray *o_array = [NSArray array];
560             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
561                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
562
563             for( i = 0; i < (int)[o_values count]; i++)
564             {
565                 NSDictionary *o_dic;
566                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
567                 o_array = [o_array arrayByAddingObject: o_dic];
568             }
569             if( b_autoplay )
570                 [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
571             else
572                 [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:YES];
573             return YES;
574         }
575     }
576     [self setNeedsDisplay:YES];
577     return YES;
578 }
579
580 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
581 {
582     [self setNeedsDisplay:YES];
583 }
584
585 @end
586
587
588 /*****************************************************************************
589  * MPSlider
590  *****************************************************************************/
591 @implementation MPSlider
592
593 void _drawKnobInRect(NSRect knobRect)
594 {
595     // Center knob in given rect
596     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
597     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
598  
599     // Draw diamond
600     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
601     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
602     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
603     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
604     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
605     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
606     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
607 }
608
609 void _drawFrameInRect(NSRect frameRect)
610 {
611     // Draw frame
612     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
613     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
614     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
615     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
616 }
617
618 - (void)drawRect:(NSRect)rect
619 {
620     // Draw default to make sure the slider behaves correctly
621     [[NSGraphicsContext currentContext] saveGraphicsState];
622     NSRectClip(NSZeroRect);
623     [super drawRect:rect];
624     [[NSGraphicsContext currentContext] restoreGraphicsState];
625  
626     // Full size
627     rect = [self bounds];
628     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
629     rect.origin.x += diff-1;
630     rect.origin.y += diff;
631     rect.size.width -= 2*diff-2;
632     rect.size.height -= 2*diff;
633  
634     // Draw dark
635     NSRect knobRect = [[self cell] knobRectFlipped:NO];
636     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
637     _drawFrameInRect(rect);
638     _drawKnobInRect(knobRect);
639  
640     // Draw shadow
641     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
642     rect.origin.x++;
643     rect.origin.y++;
644     knobRect.origin.x++;
645     knobRect.origin.y++;
646     _drawFrameInRect(rect);
647     _drawKnobInRect(knobRect);
648 }
649
650 @end
651
652
653 /*****************************************************************************
654  * ITSlider
655  *****************************************************************************/
656
657 @implementation ITSlider
658
659 - (void)awakeFromNib
660 {
661     if ([[self cell] class] != [ITSliderCell class]) {
662         // replace cell
663         NSSliderCell *oldCell = [self cell];
664         NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
665         [newCell setTag:[oldCell tag]];
666         [newCell setTarget:[oldCell target]];
667         [newCell setAction:[oldCell action]];
668         [newCell setControlSize:[oldCell controlSize]];
669         [newCell setType:[oldCell type]];
670         [newCell setState:[oldCell state]];
671         [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
672         [newCell setAltIncrementValue:[oldCell altIncrementValue]];
673         [newCell setControlTint:[oldCell controlTint]];
674         [newCell setKnobThickness:[oldCell knobThickness]];
675         [newCell setMaxValue:[oldCell maxValue]];
676         [newCell setMinValue:[oldCell minValue]];
677         [newCell setDoubleValue:[oldCell doubleValue]];
678         [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
679         [newCell setEditable:[oldCell isEditable]];
680         [newCell setEnabled:[oldCell isEnabled]];
681         [newCell setEntryType:[oldCell entryType]];
682         [newCell setHighlighted:[oldCell isHighlighted]];
683         [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
684         [self setCell:newCell];
685     }
686 }
687
688 @end
689
690 /*****************************************************************************
691  * ITSliderCell
692  *****************************************************************************/
693 @implementation ITSliderCell
694
695 - (id)init
696 {
697     self = [super init];
698     _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
699     [self controlTintChanged];
700     [[NSNotificationCenter defaultCenter] addObserver: self
701                                              selector: @selector( controlTintChanged )
702                                                  name: NSControlTintDidChangeNotification
703                                                object: nil];
704     b_mouse_down = FALSE;
705     return self;
706 }
707
708 - (void)controlTintChanged
709 {
710     if( [NSColor currentControlTint] == NSGraphiteControlTint )
711         _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
712     else
713         _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
714 }
715
716 - (void)dealloc
717 {
718     [[NSNotificationCenter defaultCenter] removeObserver: self];
719     [_knobOff release];
720     [_knobOn release];
721     [super dealloc];
722 }
723
724 - (void)drawKnob:(NSRect)knob_rect
725 {
726     NSImage *knob;
727
728     if( b_mouse_down )
729         knob = _knobOn;
730     else
731         knob = _knobOff;
732
733     [[self controlView] lockFocus];
734     [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
735         knob_rect.origin.y + knob_rect.size.height -2 )
736         operation:NSCompositeSourceOver];
737     [[self controlView] unlockFocus];
738 }
739
740 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
741         (NSView *)controlView mouseIsUp:(BOOL)flag
742 {
743     b_mouse_down = NO;
744     [self drawKnob];
745     [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
746 }
747
748 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
749 {
750     b_mouse_down = YES;
751     [self drawKnob];
752     return [super startTrackingAt:startPoint inView:controlView];
753 }
754
755 @end
756