]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/misc.m
macosx: Use input_ItemHasErrorWhenReading to display a small icon if there was an...
[vlc] / modules / gui / macosx / misc.m
index 67196e9bb267a690cb4be3846eca693eac92050c..3a033f820aed22e43137cceaba0b5a8dbccd8d9e 100644 (file)
@@ -1,10 +1,11 @@
 /*****************************************************************************
  * misc.m: code not specific to vlc
  *****************************************************************************
- * Copyright (C) 2003 VideoLAN
- * $Id: misc.m,v 1.2 2003/03/13 22:24:17 hartman Exp $
+ * Copyright (C) 2003-2008 the VideoLAN team
+ * $Id$
  *
  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
+ *          Felix Paul Kühne <fkuehne at videolan dot org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-#include <Cocoa/Cocoa.h>
+#import <Cocoa/Cocoa.h>
+#import <QuickTime/QuickTime.h>
 
-#include "misc.h"
+#import "intf.h"                                          /* VLCApplication */
+#import "misc.h"
+#import "playlist.h"
+#import "controls.h"
 
 /*****************************************************************************
- * VLBrushedMetalImageView
+ * NSImage (VLCAdditions)
+ *
+ *  Addition to NSImage
  *****************************************************************************/
+@implementation NSImage (VLCAdditions)
++ (id)imageWithSystemName:(int)name
+{
+    /* ugly Carbon stuff following...
+     * regrettably, you can't get the icons through clean Cocoa */
+
+    /* retrieve our error icon */
+    NSImage * icon;
+    IconRef ourIconRef;
+    int returnValue;
+    returnValue = GetIconRef(kOnSystemDisk, 'macs', name, &ourIconRef);
+    icon = [[[NSImage alloc] initWithSize:NSMakeSize(32,32)] autorelease];
+    [icon lockFocus];
+    CGRect rect = CGRectMake(0,0,32,32);
+    PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
+        graphicsPort],
+        &rect,
+        kAlignNone,
+        kTransformNone,
+        NULL /*inLabelColor*/,
+        kPlotIconRefNormalFlags,
+        (IconRef)ourIconRef);
+    [icon unlockFocus];
+    returnValue = ReleaseIconRef(ourIconRef);
+    return icon;
+}
 
-@implementation VLBrushedMetalImageView
++ (id)imageWithWarningIcon
+{
+    static NSImage * imageWithWarningIcon = nil;
+    if( !imageWithWarningIcon )
+    {
+        imageWithWarningIcon = [[[self class] imageWithSystemName:'caut'] retain];
+    }
+    return imageWithWarningIcon;
+}
 
-- (BOOL)mouseDownCanMoveWindow
++ (id)imageWithErrorIcon
 {
-    return YES;
+    static NSImage * imageWithErrorIcon = nil;
+    if( !imageWithErrorIcon )
+    {
+        imageWithErrorIcon = [[[self class] imageWithSystemName:'stop'] retain];
+    }
+    return imageWithErrorIcon;
 }
 
 @end
+/*****************************************************************************
+ * NSAnimation (VLCAdditions)
+ *
+ *  Missing extension to NSAnimation
+ *****************************************************************************/
+
+@implementation NSAnimation (VLCAdditions)
+/* fake class attributes  */
+static NSMapTable *VLCAdditions_userInfo = NULL;
+
++ (void)load
+{
+    /* init our fake object attribute */
+    VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
+}
+
+- (void)dealloc
+{
+    NSMapRemove(VLCAdditions_userInfo, self);
+    [super dealloc];
+}
 
+- (void)setUserInfo: (void *)userInfo
+{
+    NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
+}
+
+- (void *)userInfo
+{
+    return NSMapGet(VLCAdditions_userInfo, self);
+}
+@end
 
 /*****************************************************************************
- * MPSlider
+ * NSScreen (VLCAdditions)
+ *
+ *  Missing extension to NSScreen
  *****************************************************************************/
 
-@implementation MPSlider
+@implementation NSScreen (VLCAdditions)
+
+static NSMutableArray *blackoutWindows = NULL;
 
-+ (Class)cellClass
++ (void)load
 {
-    return( [MPSliderCell class] );
+    /* init our fake object attribute */
+    blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
+}
+
++ (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
+{
+    int i;
+    for( i = 0; i < [[NSScreen screens] count]; i++ )
+    {
+        NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
+        if([screen displayID] == displayID)
+            return screen;
+    }
+    return nil;
+}
+
+- (BOOL)isMainScreen
+{
+    return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
+}
+
+- (BOOL)isScreen: (NSScreen*)screen
+{
+    return ([self displayID] == [screen displayID]);
+}
+
+- (CGDirectDisplayID)displayID
+{
+    return (CGDirectDisplayID)_screenNumber;
+}
+
+- (void)blackoutOtherScreens
+{
+    unsigned int i;
+
+    /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
+    [blackoutWindows makeObjectsPerformSelector:@selector(close)];
+    [blackoutWindows removeAllObjects];
+
+    for(i = 0; i < [[NSScreen screens] count]; i++)
+    {
+        NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
+        VLCWindow *blackoutWindow;
+        NSRect screen_rect;
+        if([self isScreen: screen])
+            continue;
+
+        screen_rect = [screen frame];
+        screen_rect.origin.x = screen_rect.origin.y = 0;
+
+        /* blackoutWindow alloc strategy
+            - The NSMutableArray blackoutWindows has the blackoutWindow references
+            - blackoutOtherDisplays is responsible for alloc/releasing its Windows
+        */
+        blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
+                backing: NSBackingStoreBuffered defer: NO screen: screen];
+        [blackoutWindow setBackgroundColor:[NSColor blackColor]];
+        [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
+        [blackoutWindow displayIfNeeded];
+        [blackoutWindow orderFront: self animate: YES];
+
+        [blackoutWindows addObject: blackoutWindow];
+        [blackoutWindow release];
+        
+        if( [screen isMainScreen ] )
+           SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
+    }
+}
+
++ (void)unblackoutScreens
+{
+    unsigned int i;
+
+    for(i = 0; i < [blackoutWindows count]; i++)
+    {
+        VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
+        [blackoutWindow closeAndAnimate: YES];
+    }
+    
+   SetSystemUIMode( kUIModeNormal, 0);
 }
 
 @end
 
 /*****************************************************************************
- * MPSliderCell
+ * VLCWindow
+ *
+ *  Missing extension to NSWindow
  *****************************************************************************/
 
-@implementation MPSliderCell
+@implementation VLCWindow
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
+    backing:(NSBackingStoreType)backingType defer:(BOOL)flag
+{
+    self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
+    if( self )
+        b_isset_canBecomeKeyWindow = NO;
+    return self;
+}
+- (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
+{
+    b_isset_canBecomeKeyWindow = YES;
+    b_canBecomeKeyWindow = canBecomeKey;
+}
 
-- (id)init
+- (BOOL)canBecomeKeyWindow
 {
-    self = [super init];
+    if(b_isset_canBecomeKeyWindow)
+        return b_canBecomeKeyWindow;
+
+    return [super canBecomeKeyWindow];
+}
+
+- (void)closeAndAnimate: (BOOL)animate
+{
+    NSInvocation *invoc;
+    if (!animate || MACOS_VERSION < 10.4f)
+    {
+        [super close];
+        return;
+    }
+
+    invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
+    [invoc setTarget: (id)super];
+
+    if (![self isVisible] || [self alphaValue] == 0.0)
+    {
+        [super close];
+        return;
+    }
+
+    [self orderOut: self animate: YES callback: invoc];
+}
+
+- (void)orderOut: (id)sender animate: (BOOL)animate
+{
+    NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
+    [invoc setTarget: (id)super];
+    [invoc setArgument: sender atIndex: 0];
+    [self orderOut: sender animate: animate callback: invoc];
+}
+
+- (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
+{
+    NSViewAnimation *anim;
+    NSViewAnimation *current_anim;
+    NSMutableDictionary *dict;
 
-    if( self != nil )
+    if (!animate || MACOS_VERSION < 10.4f)
     {
-        _bgColor = [[NSColor colorWithDeviceRed: 0.8627451
-                                          green: 0.8784314
-                                           blue: 0.7725490
-                                          alpha: 1.0] retain];
-        _knobColor = [[NSColor blackColor] retain];
+        [self orderOut: sender];
+        return;
+    }
+
+    dict = [[NSMutableDictionary alloc] initWithCapacity:2];
+
+    [dict setObject:self forKey:NSViewAnimationTargetKey];
+
+    [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
+    anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
+    [dict release];
+
+    [anim setAnimationBlockingMode:NSAnimationNonblocking];
+    [anim setDuration:0.9];
+    [anim setFrameRate:30];
+    [anim setUserInfo: callback];
+
+    @synchronized(self) {
+        current_anim = self->animation;
+
+        if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
+        {
+            [anim release];
+        }
+        else
+        {
+            if (current_anim)
+            {
+                [current_anim stopAnimation];
+                [anim setCurrentProgress:1.0-[current_anim currentProgress]];
+                [current_anim release];
+            }
+            else
+                [anim setCurrentProgress:1.0 - [self alphaValue]];
+            self->animation = anim;
+            [self setDelegate: self];
+            [anim startAnimation];
+        }
     }
+}
+
+- (void)orderFront: (id)sender animate: (BOOL)animate
+{
+    NSViewAnimation *anim;
+    NSViewAnimation *current_anim;
+    NSMutableDictionary *dict;
+    if (!animate || MACOS_VERSION < 10.4f)
+    {
+        [super orderFront: sender];
+        [self setAlphaValue: 1.0];
+        return;
+    }
+
+    if (![self isVisible])
+    {
+        [self setAlphaValue: 0.0];
+        [super orderFront: sender];
+    }
+    else if ([self alphaValue] == 1.0)
+    {
+        [super orderFront: self];
+        return;
+    }
+
+    dict = [[NSMutableDictionary alloc] initWithCapacity:2];
+
+    [dict setObject:self forKey:NSViewAnimationTargetKey];
+    [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
+    anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
+    [dict release];
+    [anim setAnimationBlockingMode:NSAnimationNonblocking];
+    [anim setDuration:0.5];
+    [anim setFrameRate:30];
+
+    @synchronized(self) {
+        current_anim = self->animation;
+
+        if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
+        {
+            [anim release];
+        }
+        else
+        {
+            if (current_anim)
+            {
+                [current_anim stopAnimation];
+                [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
+                [current_anim release];
+            }
+            else
+                [anim setCurrentProgress:[self alphaValue]];
+            self->animation = anim;
+            [self setDelegate: self];
+            [self orderFront: sender];
+            [anim startAnimation];
+        }
+    }
+}
+
+- (void)animationDidEnd:(NSAnimation*)anim
+{
+    if ([self alphaValue] <= 0.0)
+    {
+        NSInvocation * invoc;
+        [super orderOut: nil];
+        [self setAlphaValue: 1.0];
+        if ((invoc = [anim userInfo]))
+            [invoc invoke];
+    }
+}
+@end
+
+/*****************************************************************************
+ * VLCControllerWindow
+ *****************************************************************************/
+
+@implementation VLCControllerWindow
+
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
+    backing:(NSBackingStoreType)backingType defer:(BOOL)flag
+{
+    self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
+    backing:backingType defer:flag];
+
+    [[VLCMain sharedInstance] updateTogglePlaylistState];
 
     return( self );
 }
 
+- (BOOL)performKeyEquivalent:(NSEvent *)o_event
+{
+    /* We indeed want to prioritize Cocoa key equivalent against libvlc,
+       so we perform the menu equivalent now. */
+    if([[NSApp mainMenu] performKeyEquivalent:o_event])
+        return TRUE;
+
+    return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
+           [(VLCControls *)[[VLCMain sharedInstance] getControls] keyEvent:o_event];
+}
+
+@end
+
+
+
+/*****************************************************************************
+ * VLCControllerView
+ *****************************************************************************/
+
+@implementation VLCControllerView
+
 - (void)dealloc
 {
-    [_bgColor release];
-    [_knobColor release];
+    [self unregisterDraggedTypes];
     [super dealloc];
 }
 
-- (void)setBackgroundColor:(NSColor *)newColor
+- (void)awakeFromNib
+{
+    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
+        NSFilenamesPboardType, nil]];
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
 {
-    [_bgColor release];  
-    _bgColor = [newColor retain];
+    if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
+                == NSDragOperationGeneric)
+    {
+        return NSDragOperationGeneric;
+    }
+    else
+    {
+        return NSDragOperationNone;
+    }
 }
 
-- (NSColor *)backgroundColor
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
 {
-    return( _bgColor );
+    return YES;
 }
 
-- (void)setKnobColor:(NSColor *)newColor
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 {
-    [_knobColor release];  
-    _knobColor = [newColor retain];
+    NSPasteboard *o_paste = [sender draggingPasteboard];
+    NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
+    NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
+    NSData *o_carried_data = [o_paste dataForType:o_desired_type];
+
+    if( o_carried_data )
+    {
+        if ([o_desired_type isEqualToString:NSFilenamesPboardType])
+        {
+            int i;
+            NSArray *o_array = [NSArray array];
+            NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
+                        sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
+
+            for( i = 0; i < (int)[o_values count]; i++)
+            {
+                NSDictionary *o_dic;
+                o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
+                o_array = [o_array arrayByAddingObject: o_dic];
+            }
+            [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
+            return YES;
+        }
+    }
+    [self setNeedsDisplay:YES];
+    return YES;
 }
 
-- (NSColor *)knobColor
+- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
 {
-    return( _knobColor );
+    [self setNeedsDisplay:YES];
 }
 
-- (void)setKnobThickness:(float)f_value
+@end
+
+/*****************************************************************************
+ * VLBrushedMetalImageView
+ *****************************************************************************/
+
+@implementation VLBrushedMetalImageView
+
+- (BOOL)mouseDownCanMoveWindow
 {
-    _knobThickness = f_value;
+    return YES;
 }
 
-- (float)knobThickness
+- (void)dealloc
 {
-    return( _knobThickness );
+    [self unregisterDraggedTypes];
+    [super dealloc];
 }
 
-- (NSSize)cellSizeForBounds:(NSRect)s_rc
+- (void)awakeFromNib
 {
-    return( s_rc.size );
+    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
+        NSFilenamesPboardType, nil]];
 }
 
-- (void)drawWithFrame:(NSRect)s_rc inView:(NSView *)o_view
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
 {
-    if( _scFlags.weAreVertical )
+    if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
+                == NSDragOperationGeneric)
     {
-        s_rc.origin.x = 1; s_rc.size.width -= 3;
-        s_rc.origin.y = 2; s_rc.size.height -= 5;    
+        return NSDragOperationGeneric;
     }
     else
     {
-        s_rc.origin.x = 2; s_rc.size.width -= 5;
-        s_rc.origin.y = 1; s_rc.size.height -= 3;
+        return NSDragOperationNone;
     }
+}
 
-    [super drawWithFrame: s_rc inView: o_view]; 
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
+{
+    return YES;
 }
 
-- (void)drawBarInside:(NSRect)s_rc flipped:(BOOL)b_flipped
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 {
-    NSRect s_arc;
-    s_rc.size.width += (s_rc.origin.x * 2) + 1;
-    s_rc.size.height += (s_rc.origin.y * 2) + 1;
-    s_rc.origin.x = s_rc.origin.y = 0;
+    NSPasteboard *o_paste = [sender draggingPasteboard];
+    NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
+    NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
+    NSData *o_carried_data = [o_paste dataForType:o_desired_type];
+    BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
 
-    [[NSGraphicsContext currentContext] setShouldAntialias: NO];
+    if( o_carried_data )
+    {
+        if ([o_desired_type isEqualToString:NSFilenamesPboardType])
+        {
+            int i;
+            NSArray *o_array = [NSArray array];
+            NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
+                        sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
+
+            for( i = 0; i < (int)[o_values count]; i++)
+            {
+                NSDictionary *o_dic;
+                o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
+                o_array = [o_array arrayByAddingObject: o_dic];
+            }
+            if( b_autoplay )
+                [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
+            else
+                [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:YES];
+            return YES;
+        }
+    }
+    [self setNeedsDisplay:YES];
+    return YES;
+}
+
+- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
+{
+    [self setNeedsDisplay:YES];
+}
 
-    [_bgColor set];
-    NSRectFill( s_rc );
+@end
 
-    s_arc = s_rc;
-    s_arc.origin.x += 1.5;
-    s_arc.origin.y += 1.5;
-    s_arc.size.width -= s_arc.origin.x;
-    s_arc.size.height -= s_arc.origin.y;
-    [[_bgColor shadowWithLevel: 0.1] set];
-    [NSBezierPath strokeRect: s_arc];
 
-    s_arc.origin = s_rc.origin;
-    [[NSColor blackColor] set];
-    [NSBezierPath strokeRect: s_arc];
+/*****************************************************************************
+ * MPSlider
+ *****************************************************************************/
+@implementation MPSlider
 
-    [[NSGraphicsContext currentContext] setShouldAntialias: YES];
+void _drawKnobInRect(NSRect knobRect)
+{
+    // Center knob in given rect
+    knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
+    knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
+    // Draw diamond
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
 }
 
-- (NSRect)knobRectFlipped:(BOOL)b_flipped
+void _drawFrameInRect(NSRect frameRect)
 {
-    NSSize s_size;
-    NSPoint s_pto;
-    float floatValue;
+    // Draw frame
+    NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
+    NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
+}
 
-    floatValue = [self floatValue];
+- (void)drawRect:(NSRect)rect
+{
+    // Draw default to make sure the slider behaves correctly
+    [[NSGraphicsContext currentContext] saveGraphicsState];
+    NSRectClip(NSZeroRect);
+    [super drawRect:rect];
+    [[NSGraphicsContext currentContext] restoreGraphicsState];
+    // Full size
+    rect = [self bounds];
+    int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
+    rect.origin.x += diff-1;
+    rect.origin.y += diff;
+    rect.size.width -= 2*diff-2;
+    rect.size.height -= 2*diff;
+    // Draw dark
+    NSRect knobRect = [[self cell] knobRectFlipped:NO];
+    [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
+    _drawFrameInRect(rect);
+    _drawKnobInRect(knobRect);
+    // Draw shadow
+    [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
+    rect.origin.x++;
+    rect.origin.y++;
+    knobRect.origin.x++;
+    knobRect.origin.y++;
+    _drawFrameInRect(rect);
+    _drawKnobInRect(knobRect);
+}
 
-    if( _scFlags.weAreVertical && b_flipped )
-    {
-        floatValue = _maxValue + _minValue - floatValue;
-    }
+@end
+
+
+/*****************************************************************************
+ * ITSlider
+ *****************************************************************************/
 
-    floatValue = (floatValue - _minValue) / (_maxValue - _minValue);
+@implementation ITSlider
 
-    if( _scFlags.weAreVertical )
-    {   
-        s_size = NSMakeSize( _trackRect.size.width, _knobThickness ?
-                             _knobThickness : _trackRect.size.width );
-        s_pto = _trackRect.origin;
-        s_pto.y += (_trackRect.size.height - s_size.height) * floatValue;
+- (void)awakeFromNib
+{
+    if ([[self cell] class] != [ITSliderCell class]) {
+        // replace cell
+        NSSliderCell *oldCell = [self cell];
+        NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
+        [newCell setTag:[oldCell tag]];
+        [newCell setTarget:[oldCell target]];
+        [newCell setAction:[oldCell action]];
+        [newCell setControlSize:[oldCell controlSize]];
+        [newCell setType:[oldCell type]];
+        [newCell setState:[oldCell state]];
+        [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
+        [newCell setAltIncrementValue:[oldCell altIncrementValue]];
+        [newCell setControlTint:[oldCell controlTint]];
+        [newCell setKnobThickness:[oldCell knobThickness]];
+        [newCell setMaxValue:[oldCell maxValue]];
+        [newCell setMinValue:[oldCell minValue]];
+        [newCell setDoubleValue:[oldCell doubleValue]];
+        [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
+        [newCell setEditable:[oldCell isEditable]];
+        [newCell setEnabled:[oldCell isEnabled]];
+        [newCell setEntryType:[oldCell entryType]];
+        [newCell setHighlighted:[oldCell isHighlighted]];
+        [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
+        [self setCell:newCell];
     }
+}
+
+@end
+
+/*****************************************************************************
+ * ITSliderCell
+ *****************************************************************************/
+@implementation ITSliderCell
+
+- (id)init
+{
+    self = [super init];
+    _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
+    [self controlTintChanged];
+    [[NSNotificationCenter defaultCenter] addObserver: self
+                                             selector: @selector( controlTintChanged )
+                                                 name: NSControlTintDidChangeNotification
+                                               object: nil];
+    b_mouse_down = FALSE;
+    return self;
+}
+
+- (void)controlTintChanged
+{
+    if( [NSColor currentControlTint] == NSGraphiteControlTint )
+        _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
     else
-    {   
-        s_size = NSMakeSize( _knobThickness ? _knobThickness :
-                             _trackRect.size.height, _trackRect.size.height );
-        s_pto = _trackRect.origin;
-        s_pto.x += (_trackRect.size.width - s_size.width) * floatValue;
-    }
+        _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
+}
 
-    return NSMakeRect( s_pto.x, s_pto.y, s_size.width, s_size.height );
+- (void)dealloc
+{
+    [[NSNotificationCenter defaultCenter] removeObserver: self];
+    [_knobOff release];
+    [_knobOn release];
+    [super dealloc];
 }
 
-- (void)drawKnob:(NSRect)s_rc
+- (void)drawKnob:(NSRect)knob_rect
 {
-    [[NSGraphicsContext currentContext] setShouldAntialias: NO];
+    NSImage *knob;
 
-    [_knobColor set];
-    NSRectFill( s_rc );
+    if( b_mouse_down )
+        knob = _knobOn;
+    else
+        knob = _knobOff;
 
-    [[NSGraphicsContext currentContext] setShouldAntialias: YES];
+    [[self controlView] lockFocus];
+    [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
+        knob_rect.origin.y + knob_rect.size.height -2 )
+        operation:NSCompositeSourceOver];
+    [[self controlView] unlockFocus];
+}
+
+- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
+        (NSView *)controlView mouseIsUp:(BOOL)flag
+{
+    b_mouse_down = NO;
+    [self drawKnob];
+    [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
+}
+
+- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
+{
+    b_mouse_down = YES;
+    [self drawKnob];
+    return [super startTrackingAt:startPoint inView:controlView];
 }
 
 @end
+