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