macosx: release allocated objects.
[vlc.git] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2009 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 <Carbon/Carbon.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 = nil;
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 = nil;
128
129 + (void)load
130 {
131     /* init our fake object attribute */
132     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
133 }
134
135 - (void)dealloc
136 {
137     [blackoutWindows release];
138     [super dealloc];
139 }
140
141 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
142 {
143     int i;
144  
145     for( i = 0; i < [[NSScreen screens] count]; i++ )
146     {
147         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
148         if([screen displayID] == displayID)
149             return screen;
150     }
151     return nil;
152 }
153
154 - (BOOL)isMainScreen
155 {
156     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
157 }
158
159 - (BOOL)isScreen: (NSScreen*)screen
160 {
161     return ([self displayID] == [screen displayID]);
162 }
163
164 - (CGDirectDisplayID)displayID
165 {
166         return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
167 }
168
169 - (void)blackoutOtherScreens
170 {
171     unsigned int i;
172
173     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
174     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
175     [blackoutWindows removeAllObjects];
176
177     for(i = 0; i < [[NSScreen screens] count]; i++)
178     {
179         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
180         VLCWindow *blackoutWindow;
181         NSRect screen_rect;
182  
183         if([self isScreen: screen])
184             continue;
185
186         screen_rect = [screen frame];
187         screen_rect.origin.x = screen_rect.origin.y = 0;
188
189         /* blackoutWindow alloc strategy
190             - The NSMutableArray blackoutWindows has the blackoutWindow references
191             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
192         */
193         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
194                 backing: NSBackingStoreBuffered defer: NO screen: screen];
195         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
196         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
197  
198         [blackoutWindow displayIfNeeded];
199         [blackoutWindow orderFront: self animate: YES];
200
201         [blackoutWindows addObject: blackoutWindow];
202         [blackoutWindow release];
203         
204         if( [screen isMainScreen ] )
205            SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
206     }
207 }
208
209 + (void)unblackoutScreens
210 {
211     unsigned int i;
212
213     for(i = 0; i < [blackoutWindows count]; i++)
214     {
215         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
216         [blackoutWindow closeAndAnimate: YES];
217     }
218     
219    SetSystemUIMode( kUIModeNormal, 0);
220 }
221
222 @end
223
224 /*****************************************************************************
225  * VLCWindow
226  *
227  *  Missing extension to NSWindow
228  *****************************************************************************/
229
230 @implementation VLCWindow
231 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
232     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
233 {
234     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
235     if( self )
236         b_isset_canBecomeKeyWindow = NO;
237     return self;
238 }
239 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
240 {
241     b_isset_canBecomeKeyWindow = YES;
242     b_canBecomeKeyWindow = canBecomeKey;
243 }
244
245 - (BOOL)canBecomeKeyWindow
246 {
247     if(b_isset_canBecomeKeyWindow)
248         return b_canBecomeKeyWindow;
249
250     return [super canBecomeKeyWindow];
251 }
252
253 - (void)closeAndAnimate: (BOOL)animate
254 {
255     NSInvocation *invoc;
256  
257     if (!animate)
258     {
259         [super close];
260         return;
261     }
262
263     invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
264     [invoc setTarget: (id)super];
265
266     if (![self isVisible] || [self alphaValue] == 0.0)
267     {
268         [super close];
269         return;
270     }
271
272     [self orderOut: self animate: YES callback: invoc];
273 }
274
275 - (void)orderOut: (id)sender animate: (BOOL)animate
276 {
277     NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
278     [invoc setTarget: (id)super];
279     [invoc setArgument: sender atIndex: 0];
280     [self orderOut: sender animate: animate callback: invoc];
281 }
282
283 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
284 {
285     NSViewAnimation *anim;
286     NSViewAnimation *current_anim;
287     NSMutableDictionary *dict;
288
289     if (!animate)
290     {
291         [self orderOut: sender];
292         return;
293     }
294
295     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
296
297     [dict setObject:self forKey:NSViewAnimationTargetKey];
298
299     [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
300     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
301     [dict release];
302
303     [anim setAnimationBlockingMode:NSAnimationNonblocking];
304     [anim setDuration:0.9];
305     [anim setFrameRate:30];
306     [anim setUserInfo: callback];
307
308     @synchronized(self) {
309         current_anim = self->animation;
310
311         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
312         {
313             [anim release];
314         }
315         else
316         {
317             if (current_anim)
318             {
319                 [current_anim stopAnimation];
320                 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
321                 [current_anim release];
322             }
323             else
324                 [anim setCurrentProgress:1.0 - [self alphaValue]];
325             self->animation = anim;
326             [self setDelegate: self];
327             [anim startAnimation];
328         }
329     }
330 }
331
332 - (void)orderFront: (id)sender animate: (BOOL)animate
333 {
334     NSViewAnimation *anim;
335     NSViewAnimation *current_anim;
336     NSMutableDictionary *dict;
337  
338     if (!animate)
339     {
340         [super orderFront: sender];
341         [self setAlphaValue: 1.0];
342         return;
343     }
344
345     if (![self isVisible])
346     {
347         [self setAlphaValue: 0.0];
348         [super orderFront: sender];
349     }
350     else if ([self alphaValue] == 1.0)
351     {
352         [super orderFront: self];
353         return;
354     }
355
356     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
357
358     [dict setObject:self forKey:NSViewAnimationTargetKey];
359  
360     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
361     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
362     [dict release];
363  
364     [anim setAnimationBlockingMode:NSAnimationNonblocking];
365     [anim setDuration:0.5];
366     [anim setFrameRate:30];
367
368     @synchronized(self) {
369         current_anim = self->animation;
370
371         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
372         {
373             [anim release];
374         }
375         else
376         {
377             if (current_anim)
378             {
379                 [current_anim stopAnimation];
380                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
381                 [current_anim release];
382             }
383             else
384                 [anim setCurrentProgress:[self alphaValue]];
385             self->animation = anim;
386             [self setDelegate: self];
387             [self orderFront: sender];
388             [anim startAnimation];
389         }
390     }
391 }
392
393 - (void)animationDidEnd:(NSAnimation*)anim
394 {
395     if ([self alphaValue] <= 0.0)
396     {
397         NSInvocation * invoc;
398         [super orderOut: nil];
399         [self setAlphaValue: 1.0];
400         if ((invoc = [anim userInfo]))
401             [invoc invoke];
402     }
403 }
404 @end
405
406 /*****************************************************************************
407  * VLCControllerWindow
408  *****************************************************************************/
409
410 @implementation VLCControllerWindow
411
412 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
413     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
414 {
415     /* FIXME: this should enable the SnowLeopard window style, however, it leads to ugly artifacts
416      *        needs some further investigation! -- feepk
417      BOOL b_useTextured = YES;
418
419     if( [[NSWindow class] instancesRespondToSelector:@selector(setContentBorderThickness:forEdge:)] )
420     {
421         b_useTextured = NO;
422         styleMask ^= NSTexturedBackgroundWindowMask;
423     } */
424
425     self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
426     backing:backingType defer:flag];
427
428     [[VLCMain sharedInstance] updateTogglePlaylistState];
429
430     /* FIXME: see above...
431     if(! b_useTextured )
432     {
433         [self setContentBorderThickness:28.0 forEdge:NSMinYEdge];
434     }
435     */
436     return self;
437 }
438
439 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
440 {
441     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
442        so we perform the menu equivalent now. */
443     if([[NSApp mainMenu] performKeyEquivalent:o_event])
444         return TRUE;
445
446     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
447            [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
448 }
449
450 @end
451
452
453
454 /*****************************************************************************
455  * VLCControllerView
456  *****************************************************************************/
457
458 @implementation VLCControllerView
459
460 - (void)dealloc
461 {
462     [self unregisterDraggedTypes];
463     [super dealloc];
464 }
465
466 - (void)awakeFromNib
467 {
468     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
469         NSFilenamesPboardType, nil]];
470 }
471
472 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
473 {
474     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
475                 == NSDragOperationGeneric)
476     {
477         return NSDragOperationGeneric;
478     }
479     else
480     {
481         return NSDragOperationNone;
482     }
483 }
484
485 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
486 {
487     return YES;
488 }
489
490 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
491 {
492     NSPasteboard *o_paste = [sender draggingPasteboard];
493     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
494     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
495     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
496
497     if( o_carried_data )
498     {
499         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
500         {
501             int i;
502             NSArray *o_array = [NSArray array];
503             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
504                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
505
506             for( i = 0; i < (int)[o_values count]; i++)
507             {
508                 NSDictionary *o_dic;
509                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
510                 o_array = [o_array arrayByAddingObject: o_dic];
511             }
512             [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
513             return YES;
514         }
515     }
516     [self setNeedsDisplay:YES];
517     return YES;
518 }
519
520 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
521 {
522     [self setNeedsDisplay:YES];
523 }
524
525 @end
526
527 /*****************************************************************************
528  * VLBrushedMetalImageView
529  *****************************************************************************/
530
531 @implementation VLBrushedMetalImageView
532
533 - (BOOL)mouseDownCanMoveWindow
534 {
535     return YES;
536 }
537
538 - (void)dealloc
539 {
540     [self unregisterDraggedTypes];
541     [super dealloc];
542 }
543
544 - (void)awakeFromNib
545 {
546     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
547         NSFilenamesPboardType, nil]];
548 }
549
550 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
551 {
552     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
553                 == NSDragOperationGeneric)
554     {
555         return NSDragOperationGeneric;
556     }
557     else
558     {
559         return NSDragOperationNone;
560     }
561 }
562
563 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
564 {
565     return YES;
566 }
567
568 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
569 {
570     NSPasteboard *o_paste = [sender draggingPasteboard];
571     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
572     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
573     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
574     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
575
576     if( o_carried_data )
577     {
578         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
579         {
580             int i;
581             NSArray *o_array = [NSArray array];
582             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
583                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
584
585             for( i = 0; i < (int)[o_values count]; i++)
586             {
587                 NSDictionary *o_dic;
588                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
589                 o_array = [o_array arrayByAddingObject: o_dic];
590             }
591             if( b_autoplay )
592                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
593             else
594                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
595             return YES;
596         }
597     }
598     [self setNeedsDisplay:YES];
599     return YES;
600 }
601
602 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
603 {
604     [self setNeedsDisplay:YES];
605 }
606
607 @end
608
609
610 /*****************************************************************************
611  * MPSlider
612  *****************************************************************************/
613 @implementation MPSlider
614
615 void _drawKnobInRect(NSRect knobRect)
616 {
617     // Center knob in given rect
618     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
619     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
620  
621     // Draw diamond
622     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
623     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
624     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
625     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
626     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
627     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
628     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
629 }
630
631 void _drawFrameInRect(NSRect frameRect)
632 {
633     // Draw frame
634     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
635     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
636     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
637     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
638 }
639
640 - (void)drawRect:(NSRect)rect
641 {
642     // Draw default to make sure the slider behaves correctly
643     [[NSGraphicsContext currentContext] saveGraphicsState];
644     NSRectClip(NSZeroRect);
645     [super drawRect:rect];
646     [[NSGraphicsContext currentContext] restoreGraphicsState];
647  
648     // Full size
649     rect = [self bounds];
650     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
651     rect.origin.x += diff-1;
652     rect.origin.y += diff;
653     rect.size.width -= 2*diff-2;
654     rect.size.height -= 2*diff;
655  
656     // Draw dark
657     NSRect knobRect = [[self cell] knobRectFlipped:NO];
658     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
659     _drawFrameInRect(rect);
660     _drawKnobInRect(knobRect);
661  
662     // Draw shadow
663     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
664     rect.origin.x++;
665     rect.origin.y++;
666     knobRect.origin.x++;
667     knobRect.origin.y++;
668     _drawFrameInRect(rect);
669     _drawKnobInRect(knobRect);
670 }
671
672 @end
673
674
675 /*****************************************************************************
676  * ITSlider
677  *****************************************************************************/
678
679 @implementation ITSlider
680
681 - (void)awakeFromNib
682 {
683     if ([[self cell] class] != [ITSliderCell class]) {
684         // replace cell
685         NSSliderCell *oldCell = [self cell];
686         NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
687         [newCell setTag:[oldCell tag]];
688         [newCell setTarget:[oldCell target]];
689         [newCell setAction:[oldCell action]];
690         [newCell setControlSize:[oldCell controlSize]];
691         [newCell setType:[oldCell type]];
692         [newCell setState:[oldCell state]];
693         [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
694         [newCell setAltIncrementValue:[oldCell altIncrementValue]];
695         [newCell setControlTint:[oldCell controlTint]];
696         [newCell setKnobThickness:[oldCell knobThickness]];
697         [newCell setMaxValue:[oldCell maxValue]];
698         [newCell setMinValue:[oldCell minValue]];
699         [newCell setDoubleValue:[oldCell doubleValue]];
700         [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
701         [newCell setEditable:[oldCell isEditable]];
702         [newCell setEnabled:[oldCell isEnabled]];
703         [newCell setFormatter:[oldCell formatter]];
704         [newCell setHighlighted:[oldCell isHighlighted]];
705         [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
706         [self setCell:newCell];
707     }
708 }
709
710 @end
711
712 /*****************************************************************************
713  * ITSliderCell
714  *****************************************************************************/
715 @implementation ITSliderCell
716
717 - (id)init
718 {
719     self = [super init];
720     _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
721     [self controlTintChanged];
722     [[NSNotificationCenter defaultCenter] addObserver: self
723                                              selector: @selector( controlTintChanged )
724                                                  name: NSControlTintDidChangeNotification
725                                                object: nil];
726     b_mouse_down = FALSE;
727     return self;
728 }
729
730 - (void)controlTintChanged
731 {
732     if( [NSColor currentControlTint] == NSGraphiteControlTint )
733         _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
734     else
735         _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
736 }
737
738 - (void)dealloc
739 {
740     [[NSNotificationCenter defaultCenter] removeObserver: self];
741     [_knobOff release];
742     [_knobOn release];
743     [super dealloc];
744 }
745
746 - (void)drawKnob:(NSRect)knob_rect
747 {
748     NSImage *knob;
749
750     if( b_mouse_down )
751         knob = _knobOn;
752     else
753         knob = _knobOff;
754
755     [[self controlView] lockFocus];
756     [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
757         knob_rect.origin.y + knob_rect.size.height -2 )
758         operation:NSCompositeSourceOver];
759     [[self controlView] unlockFocus];
760 }
761
762 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
763         (NSView *)controlView mouseIsUp:(BOOL)flag
764 {
765     b_mouse_down = NO;
766     [self drawKnob];
767     [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
768 }
769
770 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
771 {
772     b_mouse_down = YES;
773     [self drawKnob];
774     return [super startTrackingAt:startPoint inView:controlView];
775 }
776
777 @end
778