4d77a3824b94d39f8b7cfdbd67d1507ee8fff918
[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 = 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)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
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:(NSUInteger)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)
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)
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)
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:(NSUInteger)styleMask
407     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
408 {
409     /* FIXME: this should enable the SnowLeopard window style, however, it leads to ugly artifacts
410      *        needs some further investigation! -- feepk
411      BOOL b_useTextured = YES;
412
413     if( [[NSWindow class] instancesRespondToSelector:@selector(setContentBorderThickness:forEdge:)] )
414     {
415         b_useTextured = NO;
416         styleMask ^= NSTexturedBackgroundWindowMask;
417     } */
418
419     self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
420     backing:backingType defer:flag];
421
422     [[VLCMain sharedInstance] updateTogglePlaylistState];
423
424     /* FIXME: see above...
425     if(! b_useTextured )
426     {
427         [self setContentBorderThickness:28.0 forEdge:NSMinYEdge];
428     }
429     */
430     return self;
431 }
432
433 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
434 {
435     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
436        so we perform the menu equivalent now. */
437     if([[NSApp mainMenu] performKeyEquivalent:o_event])
438         return TRUE;
439
440     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
441            [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
442 }
443
444 @end
445
446
447
448 /*****************************************************************************
449  * VLCControllerView
450  *****************************************************************************/
451
452 @implementation VLCControllerView
453
454 - (void)dealloc
455 {
456     [self unregisterDraggedTypes];
457     [super dealloc];
458 }
459
460 - (void)awakeFromNib
461 {
462     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
463         NSFilenamesPboardType, nil]];
464 }
465
466 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
467 {
468     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
469                 == NSDragOperationGeneric)
470     {
471         return NSDragOperationGeneric;
472     }
473     else
474     {
475         return NSDragOperationNone;
476     }
477 }
478
479 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
480 {
481     return YES;
482 }
483
484 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
485 {
486     NSPasteboard *o_paste = [sender draggingPasteboard];
487     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
488     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
489     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
490
491     if( o_carried_data )
492     {
493         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
494         {
495             int i;
496             NSArray *o_array = [NSArray array];
497             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
498                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
499
500             for( i = 0; i < (int)[o_values count]; i++)
501             {
502                 NSDictionary *o_dic;
503                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
504                 o_array = [o_array arrayByAddingObject: o_dic];
505             }
506             [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
507             return YES;
508         }
509     }
510     [self setNeedsDisplay:YES];
511     return YES;
512 }
513
514 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
515 {
516     [self setNeedsDisplay:YES];
517 }
518
519 @end
520
521 /*****************************************************************************
522  * VLBrushedMetalImageView
523  *****************************************************************************/
524
525 @implementation VLBrushedMetalImageView
526
527 - (BOOL)mouseDownCanMoveWindow
528 {
529     return YES;
530 }
531
532 - (void)dealloc
533 {
534     [self unregisterDraggedTypes];
535     [super dealloc];
536 }
537
538 - (void)awakeFromNib
539 {
540     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
541         NSFilenamesPboardType, nil]];
542 }
543
544 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
545 {
546     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
547                 == NSDragOperationGeneric)
548     {
549         return NSDragOperationGeneric;
550     }
551     else
552     {
553         return NSDragOperationNone;
554     }
555 }
556
557 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
558 {
559     return YES;
560 }
561
562 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
563 {
564     NSPasteboard *o_paste = [sender draggingPasteboard];
565     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
566     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
567     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
568     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
569
570     if( o_carried_data )
571     {
572         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
573         {
574             int i;
575             NSArray *o_array = [NSArray array];
576             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
577                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
578
579             for( i = 0; i < (int)[o_values count]; i++)
580             {
581                 NSDictionary *o_dic;
582                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
583                 o_array = [o_array arrayByAddingObject: o_dic];
584             }
585             if( b_autoplay )
586                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
587             else
588                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
589             return YES;
590         }
591     }
592     [self setNeedsDisplay:YES];
593     return YES;
594 }
595
596 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
597 {
598     [self setNeedsDisplay:YES];
599 }
600
601 @end
602
603
604 /*****************************************************************************
605  * MPSlider
606  *****************************************************************************/
607 @implementation MPSlider
608
609 void _drawKnobInRect(NSRect knobRect)
610 {
611     // Center knob in given rect
612     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
613     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
614  
615     // Draw diamond
616     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
617     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
618     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
619     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
620     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
621     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
622     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
623 }
624
625 void _drawFrameInRect(NSRect frameRect)
626 {
627     // Draw frame
628     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
629     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
630     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
631     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
632 }
633
634 - (void)drawRect:(NSRect)rect
635 {
636     // Draw default to make sure the slider behaves correctly
637     [[NSGraphicsContext currentContext] saveGraphicsState];
638     NSRectClip(NSZeroRect);
639     [super drawRect:rect];
640     [[NSGraphicsContext currentContext] restoreGraphicsState];
641  
642     // Full size
643     rect = [self bounds];
644     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
645     rect.origin.x += diff-1;
646     rect.origin.y += diff;
647     rect.size.width -= 2*diff-2;
648     rect.size.height -= 2*diff;
649  
650     // Draw dark
651     NSRect knobRect = [[self cell] knobRectFlipped:NO];
652     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
653     _drawFrameInRect(rect);
654     _drawKnobInRect(knobRect);
655  
656     // Draw shadow
657     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
658     rect.origin.x++;
659     rect.origin.y++;
660     knobRect.origin.x++;
661     knobRect.origin.y++;
662     _drawFrameInRect(rect);
663     _drawKnobInRect(knobRect);
664 }
665
666 @end
667
668
669 /*****************************************************************************
670  * ITSlider
671  *****************************************************************************/
672
673 @implementation ITSlider
674
675 - (void)awakeFromNib
676 {
677     if ([[self cell] class] != [ITSliderCell class]) {
678         // replace cell
679         NSSliderCell *oldCell = [self cell];
680         NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
681         [newCell setTag:[oldCell tag]];
682         [newCell setTarget:[oldCell target]];
683         [newCell setAction:[oldCell action]];
684         [newCell setControlSize:[oldCell controlSize]];
685         [newCell setType:[oldCell type]];
686         [newCell setState:[oldCell state]];
687         [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
688         [newCell setAltIncrementValue:[oldCell altIncrementValue]];
689         [newCell setControlTint:[oldCell controlTint]];
690         [newCell setKnobThickness:[oldCell knobThickness]];
691         [newCell setMaxValue:[oldCell maxValue]];
692         [newCell setMinValue:[oldCell minValue]];
693         [newCell setDoubleValue:[oldCell doubleValue]];
694         [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
695         [newCell setEditable:[oldCell isEditable]];
696         [newCell setEnabled:[oldCell isEnabled]];
697         [newCell setFormatter:[oldCell formatter]];
698         [newCell setHighlighted:[oldCell isHighlighted]];
699         [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
700         [self setCell:newCell];
701     }
702 }
703
704 @end
705
706 /*****************************************************************************
707  * ITSliderCell
708  *****************************************************************************/
709 @implementation ITSliderCell
710
711 - (id)init
712 {
713     self = [super init];
714     _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
715     [self controlTintChanged];
716     [[NSNotificationCenter defaultCenter] addObserver: self
717                                              selector: @selector( controlTintChanged )
718                                                  name: NSControlTintDidChangeNotification
719                                                object: nil];
720     b_mouse_down = FALSE;
721     return self;
722 }
723
724 - (void)controlTintChanged
725 {
726     if( [NSColor currentControlTint] == NSGraphiteControlTint )
727         _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
728     else
729         _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
730 }
731
732 - (void)dealloc
733 {
734     [[NSNotificationCenter defaultCenter] removeObserver: self];
735     [_knobOff release];
736     [_knobOn release];
737     [super dealloc];
738 }
739
740 - (void)drawKnob:(NSRect)knob_rect
741 {
742     NSImage *knob;
743
744     if( b_mouse_down )
745         knob = _knobOn;
746     else
747         knob = _knobOff;
748
749     [[self controlView] lockFocus];
750     [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
751         knob_rect.origin.y + knob_rect.size.height -2 )
752         operation:NSCompositeSourceOver];
753     [[self controlView] unlockFocus];
754 }
755
756 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
757         (NSView *)controlView mouseIsUp:(BOOL)flag
758 {
759     b_mouse_down = NO;
760     [self drawKnob];
761     [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
762 }
763
764 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
765 {
766     b_mouse_down = YES;
767     [self drawKnob];
768     return [super startTrackingAt:startPoint inView:controlView];
769 }
770
771 @end
772