]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
macosx: fixed incorrect URL creation (closes #4966)
[vlc] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2011 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 #import <vlc_url.h>
33
34 /*****************************************************************************
35  * NSImage (VLCAdditions)
36  *
37  *  Addition to NSImage
38  *****************************************************************************/
39 @implementation NSImage (VLCAdditions)
40 + (id)imageWithSystemName:(int)name
41 {
42     /* ugly Carbon stuff following...
43      * regrettably, you can't get the icons through clean Cocoa */
44
45     /* retrieve our error icon */
46     NSImage * icon;
47     IconRef ourIconRef;
48     int returnValue;
49     returnValue = GetIconRef(kOnSystemDisk, 'macs', name, &ourIconRef);
50     icon = [[[NSImage alloc] initWithSize:NSMakeSize(32,32)] autorelease];
51     [icon lockFocus];
52     CGRect rect = CGRectMake(0,0,32,32);
53     PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
54         graphicsPort],
55         &rect,
56         kAlignNone,
57         kTransformNone,
58         NULL /*inLabelColor*/,
59         kPlotIconRefNormalFlags,
60         (IconRef)ourIconRef);
61     [icon unlockFocus];
62     returnValue = ReleaseIconRef(ourIconRef);
63     return icon;
64 }
65
66 + (id)imageWithWarningIcon
67 {
68     static NSImage * imageWithWarningIcon = nil;
69     if( !imageWithWarningIcon )
70     {
71         imageWithWarningIcon = [[[self class] imageWithSystemName:'caut'] retain];
72     }
73     return imageWithWarningIcon;
74 }
75
76 + (id)imageWithErrorIcon
77 {
78     static NSImage * imageWithErrorIcon = nil;
79     if( !imageWithErrorIcon )
80     {
81         imageWithErrorIcon = [[[self class] imageWithSystemName:'stop'] retain];
82     }
83     return imageWithErrorIcon;
84 }
85
86 @end
87 /*****************************************************************************
88  * NSAnimation (VLCAdditions)
89  *
90  *  Missing extension to NSAnimation
91  *****************************************************************************/
92
93 @implementation NSAnimation (VLCAdditions)
94 /* fake class attributes  */
95 static NSMapTable *VLCAdditions_userInfo = NULL;
96
97 + (void)load
98 {
99     /* init our fake object attribute */
100     VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
101 }
102
103 - (void)dealloc
104 {
105     NSMapRemove(VLCAdditions_userInfo, self);
106     [super dealloc];
107 }
108
109 - (void)setUserInfo: (void *)userInfo
110 {
111     NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
112 }
113
114 - (void *)userInfo
115 {
116     return NSMapGet(VLCAdditions_userInfo, self);
117 }
118 @end
119
120 /*****************************************************************************
121  * NSScreen (VLCAdditions)
122  *
123  *  Missing extension to NSScreen
124  *****************************************************************************/
125
126 @implementation NSScreen (VLCAdditions)
127
128 static NSMutableArray *blackoutWindows = NULL;
129
130 + (void)load
131 {
132     /* init our fake object attribute */
133     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
134 }
135
136 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
137 {
138     int i;
139  
140     for( i = 0; i < [[NSScreen screens] count]; i++ )
141     {
142         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
143         if([screen displayID] == displayID)
144             return screen;
145     }
146     return nil;
147 }
148
149 - (BOOL)isMainScreen
150 {
151     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
152 }
153
154 - (BOOL)isScreen: (NSScreen*)screen
155 {
156     return ([self displayID] == [screen displayID]);
157 }
158
159 - (CGDirectDisplayID)displayID
160 {
161         return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
162 }
163
164 - (void)blackoutOtherScreens
165 {
166     unsigned int i;
167
168     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
169     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
170     [blackoutWindows removeAllObjects];
171
172     for(i = 0; i < [[NSScreen screens] count]; i++)
173     {
174         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
175         VLCWindow *blackoutWindow;
176         NSRect screen_rect;
177  
178         if([self isScreen: screen])
179             continue;
180
181         screen_rect = [screen frame];
182         screen_rect.origin.x = screen_rect.origin.y = 0;
183
184         /* blackoutWindow alloc strategy
185             - The NSMutableArray blackoutWindows has the blackoutWindow references
186             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
187         */
188         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
189                 backing: NSBackingStoreBuffered defer: NO screen: screen];
190         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
191         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
192  
193         [blackoutWindow displayIfNeeded];
194         [blackoutWindow orderFront: self animate: YES];
195
196         [blackoutWindows addObject: blackoutWindow];
197         [blackoutWindow release];
198         
199         if( [screen isMainScreen ] )
200            SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
201     }
202 }
203
204 + (void)unblackoutScreens
205 {
206     unsigned int i;
207
208     for(i = 0; i < [blackoutWindows count]; i++)
209     {
210         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
211         [blackoutWindow closeAndAnimate: YES];
212     }
213     
214    SetSystemUIMode( kUIModeNormal, 0);
215 }
216
217 @end
218
219 /*****************************************************************************
220  * VLCWindow
221  *
222  *  Missing extension to NSWindow
223  *****************************************************************************/
224
225 @implementation VLCWindow
226 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
227     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
228 {
229     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
230     if( self )
231     {
232         b_isset_canBecomeKeyWindow = NO;
233         /* we don't want this window to be restored on relaunch */
234         if ([self respondsToSelector:@selector(setRestorable:)])
235             [self setRestorable:NO];
236     }
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: self];
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: self];
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
437     /* we don't want this window to be restored on relaunch */
438     if ([self respondsToSelector:@selector(setRestorable:)])
439         [self setRestorable:NO];
440
441     return self;
442 }
443
444 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
445 {
446     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
447        so we perform the menu equivalent now. */
448     if([[NSApp mainMenu] performKeyEquivalent:o_event])
449         return TRUE;
450
451     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
452            [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
453 }
454
455 @end
456
457
458
459 /*****************************************************************************
460  * VLCControllerView
461  *****************************************************************************/
462
463 @implementation VLCControllerView
464
465 - (void)dealloc
466 {
467     [self unregisterDraggedTypes];
468     [super dealloc];
469 }
470
471 - (void)awakeFromNib
472 {
473     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
474         NSFilenamesPboardType, nil]];
475 }
476
477 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
478 {
479     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
480                 == NSDragOperationGeneric)
481     {
482         return NSDragOperationGeneric;
483     }
484     else
485     {
486         return NSDragOperationNone;
487     }
488 }
489
490 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
491 {
492     return YES;
493 }
494
495 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
496 {
497     NSPasteboard *o_paste = [sender draggingPasteboard];
498     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
499     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
500     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
501
502     if( o_carried_data )
503     {
504         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
505         {
506             int i;
507             NSArray *o_array = [NSArray array];
508             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
509                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
510
511             for( i = 0; i < (int)[o_values count]; i++)
512             {
513                 NSDictionary *o_dic;
514                 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
515                 if( !psz_uri )
516                     continue;
517
518                 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
519
520                 free( psz_uri );
521                 o_array = [o_array arrayByAddingObject: o_dic];
522             }
523             [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
524             return YES;
525         }
526     }
527     [self setNeedsDisplay:YES];
528     return YES;
529 }
530
531 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
532 {
533     [self setNeedsDisplay:YES];
534 }
535
536 @end
537
538 /*****************************************************************************
539  * VLBrushedMetalImageView
540  *****************************************************************************/
541
542 @implementation VLBrushedMetalImageView
543
544 - (BOOL)mouseDownCanMoveWindow
545 {
546     return YES;
547 }
548
549 - (void)dealloc
550 {
551     [self unregisterDraggedTypes];
552     [super dealloc];
553 }
554
555 - (void)awakeFromNib
556 {
557     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
558         NSFilenamesPboardType, nil]];
559 }
560
561 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
562 {
563     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
564                 == NSDragOperationGeneric)
565     {
566         return NSDragOperationGeneric;
567     }
568     else
569     {
570         return NSDragOperationNone;
571     }
572 }
573
574 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
575 {
576     return YES;
577 }
578
579 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
580 {
581     NSPasteboard *o_paste = [sender draggingPasteboard];
582     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
583     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
584     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
585     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
586
587     if( o_carried_data )
588     {
589         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
590         {
591             int i;
592             NSArray *o_array = [NSArray array];
593             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
594                         sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
595
596             for( i = 0; i < (int)[o_values count]; i++)
597             {
598                 NSDictionary *o_dic;
599                 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
600                 if( !psz_uri )
601                     continue;
602
603                 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
604                 free( psz_uri );
605
606                 o_array = [o_array arrayByAddingObject: o_dic];
607             }
608             if( b_autoplay )
609                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
610             else
611                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
612             return YES;
613         }
614     }
615     [self setNeedsDisplay:YES];
616     return YES;
617 }
618
619 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
620 {
621     [self setNeedsDisplay:YES];
622 }
623
624 @end
625
626
627 /*****************************************************************************
628  * MPSlider
629  *****************************************************************************/
630 @implementation MPSlider
631
632 void _drawKnobInRect(NSRect knobRect)
633 {
634     // Center knob in given rect
635     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
636     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
637  
638     // Draw diamond
639     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
640     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
641     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
642     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
643     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
644     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
645     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
646 }
647
648 void _drawFrameInRect(NSRect frameRect)
649 {
650     // Draw frame
651     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
652     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
653     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
654     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
655 }
656
657 - (void)drawRect:(NSRect)rect
658 {
659     // Draw default to make sure the slider behaves correctly
660     [[NSGraphicsContext currentContext] saveGraphicsState];
661     NSRectClip(NSZeroRect);
662     [super drawRect:rect];
663     [[NSGraphicsContext currentContext] restoreGraphicsState];
664  
665     // Full size
666     rect = [self bounds];
667     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
668     rect.origin.x += diff-1;
669     rect.origin.y += diff;
670     rect.size.width -= 2*diff-2;
671     rect.size.height -= 2*diff;
672  
673     // Draw dark
674     NSRect knobRect = [[self cell] knobRectFlipped:NO];
675     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
676     _drawFrameInRect(rect);
677     _drawKnobInRect(knobRect);
678  
679     // Draw shadow
680     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
681     rect.origin.x++;
682     rect.origin.y++;
683     knobRect.origin.x++;
684     knobRect.origin.y++;
685     _drawFrameInRect(rect);
686     _drawKnobInRect(knobRect);
687 }
688
689 @end
690
691
692 /*****************************************************************************
693  * ITSlider
694  *****************************************************************************/
695
696 @implementation ITSlider
697
698 - (void)awakeFromNib
699 {
700     if ([[self cell] class] != [ITSliderCell class]) {
701         // replace cell
702         NSSliderCell *oldCell = [self cell];
703         NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
704         [newCell setTag:[oldCell tag]];
705         [newCell setTarget:[oldCell target]];
706         [newCell setAction:[oldCell action]];
707         [newCell setControlSize:[oldCell controlSize]];
708         [newCell setType:[oldCell type]];
709         [newCell setState:[oldCell state]];
710         [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
711         [newCell setAltIncrementValue:[oldCell altIncrementValue]];
712         [newCell setControlTint:[oldCell controlTint]];
713         [newCell setKnobThickness:[oldCell knobThickness]];
714         [newCell setMaxValue:[oldCell maxValue]];
715         [newCell setMinValue:[oldCell minValue]];
716         [newCell setDoubleValue:[oldCell doubleValue]];
717         [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
718         [newCell setEditable:[oldCell isEditable]];
719         [newCell setEnabled:[oldCell isEnabled]];
720         [newCell setFormatter:[oldCell formatter]];
721         [newCell setHighlighted:[oldCell isHighlighted]];
722         [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
723         [self setCell:newCell];
724     }
725 }
726
727 @end
728
729 /*************************************************************************** **
730  * ITSliderCell
731  *****************************************************************************/
732 @implementation ITSliderCell
733
734 - (id)init
735 {
736     self = [super init];
737     _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
738         _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
739     b_mouse_down = FALSE;
740     return self;
741 }
742
743
744
745 - (void)dealloc
746 {
747     [[NSNotificationCenter defaultCenter] removeObserver: self];
748     [_knobOff release];
749     [_knobOn release];
750     [super dealloc];
751 }
752
753 - (void)drawKnob:(NSRect)knob_rect
754 {
755     NSImage *knob;
756
757     if( b_mouse_down )
758         knob = _knobOn;
759     else
760         knob = _knobOff;
761
762     [[self controlView] lockFocus];
763     [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
764         knob_rect.origin.y + knob_rect.size.height -2 )
765         operation:NSCompositeSourceOver];
766     [[self controlView] unlockFocus];
767 }
768
769 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
770         (NSView *)controlView mouseIsUp:(BOOL)flag
771 {
772     b_mouse_down = NO;
773     [self drawKnob];
774     [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
775 }
776
777 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
778 {
779     b_mouse_down = YES;
780     [self drawKnob];
781     return [super startTrackingAt:startPoint inView:controlView];
782 }
783
784 @end
785