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