]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
d1211c9a816c82cdea0699aa7480a03be9e58d70
[vlc] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2011 VLC authors and VideoLAN
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 "CompatibilityFixes.h"
29 #import "intf.h"                                          /* VLCApplication */
30 #import "MainWindow.h"
31 #import "misc.h"
32 #import "playlist.h"
33 #import "controls.h"
34 #import <vlc_url.h>
35
36 /*****************************************************************************
37  * NSAnimation (VLCAdditions)
38  *
39  *  Missing extension to NSAnimation
40  *****************************************************************************/
41
42 @implementation NSAnimation (VLCAdditions)
43 /* fake class attributes  */
44 static NSMapTable *VLCAdditions_userInfo = NULL;
45
46 + (void)load
47 {
48     /* init our fake object attribute */
49     VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
50 }
51
52 - (void)dealloc
53 {
54     NSMapRemove(VLCAdditions_userInfo, self);
55     [super dealloc];
56 }
57
58 - (void)setUserInfo: (void *)userInfo
59 {
60     NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
61 }
62
63 - (void *)userInfo
64 {
65     return NSMapGet(VLCAdditions_userInfo, self);
66 }
67 @end
68
69 /*****************************************************************************
70  * NSScreen (VLCAdditions)
71  *
72  *  Missing extension to NSScreen
73  *****************************************************************************/
74
75 @implementation NSScreen (VLCAdditions)
76
77 static NSMutableArray *blackoutWindows = NULL;
78
79 + (void)load
80 {
81     /* init our fake object attribute */
82     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
83 }
84
85 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
86 {
87     NSUInteger count = [[NSScreen screens] count];
88
89     for( NSUInteger i = 0; i < count; i++ )
90     {
91         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
92         if([screen displayID] == displayID)
93             return screen;
94     }
95     return nil;
96 }
97
98 - (BOOL)isMainScreen
99 {
100     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
101 }
102
103 - (BOOL)isScreen: (NSScreen*)screen
104 {
105     return ([self displayID] == [screen displayID]);
106 }
107
108 - (CGDirectDisplayID)displayID
109 {
110         return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
111 }
112
113 - (void)blackoutOtherScreens
114 {
115     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
116     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
117     [blackoutWindows removeAllObjects];
118
119     NSUInteger screenCount = [[NSScreen screens] count];
120     for(NSUInteger i = 0; i < screenCount; i++)
121     {
122         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
123         VLCWindow *blackoutWindow;
124         NSRect screen_rect;
125
126         if([self isScreen: screen])
127             continue;
128
129         screen_rect = [screen frame];
130         screen_rect.origin.x = screen_rect.origin.y = 0;
131
132         /* blackoutWindow alloc strategy
133             - The NSMutableArray blackoutWindows has the blackoutWindow references
134             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
135         */
136         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
137                 backing: NSBackingStoreBuffered defer: NO screen: screen];
138         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
139         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
140
141         [blackoutWindow displayIfNeeded];
142         [blackoutWindow orderFront: self animate: YES];
143
144         [blackoutWindows addObject: blackoutWindow];
145         [blackoutWindow release];
146
147         if( [screen isMainScreen ] )
148         {
149             if ([screen isMainScreen])
150             {
151                 if (OSX_LEOPARD)
152                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
153                 else
154                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
155             }
156         }
157     }
158 }
159
160 + (void)unblackoutScreens
161 {
162     NSUInteger blackoutWindowCount = [blackoutWindows count];
163
164     for(NSUInteger i = 0; i < blackoutWindowCount; i++)
165     {
166         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
167         [blackoutWindow closeAndAnimate: YES];
168     }
169
170     if (OSX_LEOPARD)
171         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
172     else
173         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
174 }
175
176 @end
177
178 /*****************************************************************************
179  * VLCWindow
180  *
181  *  Missing extension to NSWindow
182  *****************************************************************************/
183
184 @implementation VLCWindow
185 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
186     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
187 {
188     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
189     if( self )
190     {
191         b_isFullscreen = NO;
192         b_isset_canBecomeKeyWindow = NO;
193         /* we don't want this window to be restored on relaunch */
194         if (OSX_LION)
195             [self setRestorable:NO];
196     }
197     return self;
198 }
199 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
200 {
201     b_isset_canBecomeKeyWindow = YES;
202     b_canBecomeKeyWindow = canBecomeKey;
203 }
204
205 - (BOOL)canBecomeKeyWindow
206 {
207     if(b_isset_canBecomeKeyWindow)
208         return b_canBecomeKeyWindow;
209
210     return [super canBecomeKeyWindow];
211 }
212
213 - (void)closeAndAnimate: (BOOL)animate
214 {
215     NSInvocation *invoc;
216
217     if (!animate)
218     {
219         [super close];
220         return;
221     }
222
223     invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
224     [invoc setTarget: self];
225
226     if (![self isVisible] || [self alphaValue] == 0.0)
227     {
228         [super close];
229         return;
230     }
231
232     [self orderOut: self animate: YES callback: invoc];
233 }
234
235 - (void)orderOut: (id)sender animate: (BOOL)animate
236 {
237     NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
238     [invoc setTarget: self];
239     [invoc setArgument: sender atIndex: 0];
240     [self orderOut: sender animate: animate callback: invoc];
241 }
242
243 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
244 {
245     NSViewAnimation *anim;
246     NSViewAnimation *current_anim;
247     NSMutableDictionary *dict;
248
249     if (!animate)
250     {
251         [self orderOut: sender];
252         return;
253     }
254
255     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
256
257     [dict setObject:self forKey:NSViewAnimationTargetKey];
258
259     [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
260     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
261     [dict release];
262
263     [anim setAnimationBlockingMode:NSAnimationNonblocking];
264     [anim setDuration:0.9];
265     [anim setFrameRate:30];
266     [anim setUserInfo: callback];
267
268     @synchronized(self) {
269         current_anim = self->animation;
270
271         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
272         {
273             [anim release];
274         }
275         else
276         {
277             if (current_anim)
278             {
279                 [current_anim stopAnimation];
280                 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
281                 [current_anim release];
282             }
283             else
284                 [anim setCurrentProgress:1.0 - [self alphaValue]];
285             self->animation = anim;
286             [self setDelegate: self];
287             [anim startAnimation];
288         }
289     }
290 }
291
292 - (void)orderFront: (id)sender animate: (BOOL)animate
293 {
294     NSViewAnimation *anim;
295     NSViewAnimation *current_anim;
296     NSMutableDictionary *dict;
297
298     if (!animate)
299     {
300         [super orderFront: sender];
301         [self setAlphaValue: 1.0];
302         return;
303     }
304
305     if (![self isVisible])
306     {
307         [self setAlphaValue: 0.0];
308         [super orderFront: sender];
309     }
310     else if ([self alphaValue] == 1.0)
311     {
312         [super orderFront: self];
313         return;
314     }
315
316     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
317
318     [dict setObject:self forKey:NSViewAnimationTargetKey];
319
320     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
321     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
322     [dict release];
323
324     [anim setAnimationBlockingMode:NSAnimationNonblocking];
325     [anim setDuration:0.5];
326     [anim setFrameRate:30];
327
328     @synchronized(self) {
329         current_anim = self->animation;
330
331         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
332         {
333             [anim release];
334         }
335         else
336         {
337             if (current_anim)
338             {
339                 [current_anim stopAnimation];
340                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
341                 [current_anim release];
342             }
343             else
344                 [anim setCurrentProgress:[self alphaValue]];
345             self->animation = anim;
346             [self setDelegate: self];
347             [self orderFront: sender];
348             [anim startAnimation];
349         }
350     }
351 }
352
353 - (void)animationDidEnd:(NSAnimation*)anim
354 {
355     if ([self alphaValue] <= 0.0)
356     {
357         NSInvocation * invoc;
358         [super orderOut: nil];
359         [self setAlphaValue: 1.0];
360         if ((invoc = [anim userInfo]))
361             [invoc invoke];
362     }
363 }
364
365 - (void)setFullscreen:(BOOL)b_var
366 {
367     b_isFullscreen = b_var;
368 }
369
370 - (BOOL)isFullscreen
371 {
372     return b_isFullscreen;
373 }
374
375 @end
376
377 /*****************************************************************************
378  * VLCControllerView
379  *****************************************************************************/
380
381 @implementation VLCControllerView
382
383 - (void)dealloc
384 {
385     [self unregisterDraggedTypes];
386     [super dealloc];
387 }
388
389 - (void)awakeFromNib
390 {
391     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
392         NSFilenamesPboardType, nil]];
393 }
394
395 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
396 {
397     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
398                 == NSDragOperationGeneric)
399     {
400         return NSDragOperationGeneric;
401     }
402     else
403     {
404         return NSDragOperationNone;
405     }
406 }
407
408 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
409 {
410     return YES;
411 }
412
413 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
414 {
415     NSPasteboard *o_paste = [sender draggingPasteboard];
416     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
417     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
418     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
419
420     if( o_carried_data )
421     {
422         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
423         {
424             NSArray *o_array = [NSArray array];
425             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
426             NSUInteger count = [o_values count];
427
428             for( NSUInteger i = 0; i < count; i++)
429             {
430                 NSDictionary *o_dic;
431                 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
432                 if( !psz_uri )
433                     continue;
434
435                 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
436
437                 free( psz_uri );
438                 o_array = [o_array arrayByAddingObject: o_dic];
439             }
440             [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
441             return YES;
442         }
443     }
444     [self setNeedsDisplay:YES];
445     return YES;
446 }
447
448 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
449 {
450     [self setNeedsDisplay:YES];
451 }
452
453 @end
454
455 /*****************************************************************************
456  * VLBrushedMetalImageView
457  *****************************************************************************/
458
459 @implementation VLBrushedMetalImageView
460
461 - (BOOL)mouseDownCanMoveWindow
462 {
463     return YES;
464 }
465
466 - (void)dealloc
467 {
468     [self unregisterDraggedTypes];
469     [super dealloc];
470 }
471
472 - (void)awakeFromNib
473 {
474     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
475         NSFilenamesPboardType, nil]];
476     [self setImageScaling: NSScaleToFit];
477     [self setImageFrameStyle: NSImageFrameNone];
478     [self setImageAlignment: NSImageAlignCenter];
479 }
480
481 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
482 {
483     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
484                 == NSDragOperationGeneric)
485     {
486         return NSDragOperationGeneric;
487     }
488     else
489     {
490         return NSDragOperationNone;
491     }
492 }
493
494 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
495 {
496     return YES;
497 }
498
499 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
500 {
501     NSPasteboard *o_paste = [sender draggingPasteboard];
502     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
503     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
504     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
505     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
506
507     if( o_carried_data )
508     {
509         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
510         {
511             NSArray *o_array = [NSArray array];
512             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
513             NSUInteger count = [o_values count];
514
515             for( NSUInteger i = 0; i < count; i++)
516             {
517                 NSDictionary *o_dic;
518                 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
519                 if( !psz_uri )
520                     continue;
521
522                 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
523                 free( psz_uri );
524
525                 o_array = [o_array arrayByAddingObject: o_dic];
526             }
527             if( b_autoplay )
528                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
529             else
530                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
531             return YES;
532         }
533     }
534     [self setNeedsDisplay:YES];
535     return YES;
536 }
537
538 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
539 {
540     [self setNeedsDisplay:YES];
541 }
542
543 @end
544
545
546 /*****************************************************************************
547  * MPSlider
548  *****************************************************************************/
549 @implementation MPSlider
550
551 void _drawKnobInRect(NSRect knobRect)
552 {
553     // Center knob in given rect
554     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
555     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
556
557     // Draw diamond
558     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
559     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
560     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
561     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
562     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
563     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
564     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
565 }
566
567 void _drawFrameInRect(NSRect frameRect)
568 {
569     // Draw frame
570     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
571     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
572     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
573     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
574 }
575
576 - (void)drawRect:(NSRect)rect
577 {
578     // Draw default to make sure the slider behaves correctly
579     [[NSGraphicsContext currentContext] saveGraphicsState];
580     NSRectClip(NSZeroRect);
581     [super drawRect:rect];
582     [[NSGraphicsContext currentContext] restoreGraphicsState];
583
584     // Full size
585     rect = [self bounds];
586     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
587     rect.origin.x += diff-1;
588     rect.origin.y += diff;
589     rect.size.width -= 2*diff-2;
590     rect.size.height -= 2*diff;
591
592     // Draw dark
593     NSRect knobRect = [[self cell] knobRectFlipped:NO];
594     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
595     _drawFrameInRect(rect);
596     _drawKnobInRect(knobRect);
597
598     // Draw shadow
599     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
600     rect.origin.x++;
601     rect.origin.y++;
602     knobRect.origin.x++;
603     knobRect.origin.y++;
604     _drawFrameInRect(rect);
605     _drawKnobInRect(knobRect);
606 }
607
608 @end
609
610 /*****************************************************************************
611  * TimeLineSlider
612  *****************************************************************************/
613
614 @implementation TimeLineSlider
615
616 - (void)awakeFromNib
617 {
618     if (config_GetInt( VLCIntf, "macosx-interfacestyle" ))
619         o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
620     else
621         o_knob_img = [NSImage imageNamed:@"progression-knob"];
622     img_rect.size = [o_knob_img size];
623     img_rect.origin.x = img_rect.origin.y = 0;
624 }
625
626 - (void)dealloc
627 {
628     [o_knob_img release];
629     [super dealloc];
630 }
631
632 - (CGFloat)knobPosition
633 {
634     NSRect knobRect = [[self cell] knobRectFlipped:NO];
635     knobRect.origin.x += knobRect.size.width / 2;
636     return knobRect.origin.x;
637 }
638
639 - (void)drawKnobInRect:(NSRect)knobRect
640 {
641     knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
642     knobRect.size.width = img_rect.size.width;
643     knobRect.size.height = img_rect.size.height;
644     [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
645 }
646
647 - (void)drawRect:(NSRect)rect
648 {
649     /* Draw default to make sure the slider behaves correctly */
650     [[NSGraphicsContext currentContext] saveGraphicsState];
651     NSRectClip(NSZeroRect);
652     [super drawRect:rect];
653     [[NSGraphicsContext currentContext] restoreGraphicsState];
654
655     NSRect knobRect = [[self cell] knobRectFlipped:NO];
656     knobRect.origin.y+=1;
657     [self drawKnobInRect: knobRect];
658 }
659
660 @end
661
662 /*****************************************************************************
663  * ITSlider
664  *****************************************************************************/
665
666 @implementation ITSlider
667
668 - (void)awakeFromNib
669 {
670     BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
671     if (b_dark)
672         img = [NSImage imageNamed:@"volume-slider-knob_dark"];
673     else
674         img = [NSImage imageNamed:@"volume-slider-knob"];
675
676     image_rect.size = [img size];
677     image_rect.origin.x = 0;
678
679     if (b_dark)
680         image_rect.origin.y = -1;
681     else
682         image_rect.origin.y = 0;
683 }
684
685 - (void)drawKnobInRect:(NSRect)knobRect
686 {
687     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
688     knobRect.size.width = image_rect.size.width;
689     knobRect.size.height = image_rect.size.height;
690     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
691 }
692
693 - (void)drawRect:(NSRect)rect
694 {
695     /* Draw default to make sure the slider behaves correctly */
696     [[NSGraphicsContext currentContext] saveGraphicsState];
697     NSRectClip(NSZeroRect);
698     [super drawRect:rect];
699     [[NSGraphicsContext currentContext] restoreGraphicsState];
700
701     NSRect knobRect = [[self cell] knobRectFlipped:NO];
702     knobRect.origin.y+=2;
703     [self drawKnobInRect: knobRect];
704 }
705
706 @end
707
708 /*****************************************************************************
709  * VLCTimeField implementation
710  *****************************************************************************
711  * we need this to catch our click-event in the controller window
712  *****************************************************************************/
713
714 @implementation VLCTimeField
715 + (void)initialize{
716     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
717     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
718
719     [defaults registerDefaults:appDefaults];
720 }
721
722 - (void)awakeFromNib
723 {
724     NSColor *o_string_color;
725     if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
726         o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
727     else
728         o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
729
730     textAlignment = NSCenterTextAlignment;
731     o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
732 }
733
734 - (void)setAlignment:(NSTextAlignment)alignment
735 {
736     textAlignment = alignment;
737     [self setStringValue:[self stringValue]];
738 }
739
740 - (void)dealloc
741 {
742     [o_string_shadow release];
743     [o_string_attributes_dict release];
744 }
745
746 - (void)setStringValue:(NSString *)string
747 {
748     if (!o_string_shadow)
749     {
750         o_string_shadow = [[NSShadow alloc] init];
751         [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
752         [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.5)];
753         [o_string_shadow setShadowBlurRadius:0.0];
754     }
755
756     NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
757     NSUInteger i_stringLength = [string length];
758
759     [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
760     [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
761     [self setAttributedStringValue: o_attributed_string];
762     [o_attributed_string release];
763 }
764
765 - (void)mouseDown: (NSEvent *)ourEvent
766 {
767     if( [ourEvent clickCount] > 1 )
768         [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
769     else
770     {
771         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
772             [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
773         else
774             [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
775     }
776 }
777
778 - (BOOL)timeRemaining
779 {
780     return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
781 }
782 @end
783
784 /*****************************************************************************
785  * VLCMainWindowSplitView implementation
786  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
787  *****************************************************************************/
788 @implementation VLCMainWindowSplitView : NSSplitView
789 /* Return the color of the dividers that the split view is drawing between subviews. The default implementation of this method returns [NSColor clearColor] for the thick divider style. It will also return [NSColor clearColor] for the thin divider style when the split view is in a textured window. All other thin dividers are drawn with a color that looks good between two white panes. You can override this method to change the color of dividers.
790  */
791 - (NSColor *)dividerColor
792 {
793     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
794 }
795
796 /* Return the thickness of the dividers that the split view is drawing between subviews. The default implementation returns a value that depends on the divider style. You can override this method to change the size of dividers.
797  */
798 - (CGFloat)dividerThickness
799 {
800     return 1.0;
801 }
802
803 - (void)adjustSubviews
804 {
805     NSArray *o_subviews = [self subviews];
806     NSRect viewDimensions = [self frame];
807     NSRect leftViewDimensions = [[o_subviews objectAtIndex:0] frame];
808     NSRect rightViewDimensions = [[o_subviews objectAtIndex:1] frame];
809     CGFloat f_dividerThickness = [self dividerThickness];
810
811     leftViewDimensions.size.height = viewDimensions.size.height;
812     [[o_subviews objectAtIndex:0] setFrame: leftViewDimensions];
813
814     rightViewDimensions.origin.x = leftViewDimensions.size.width + f_dividerThickness;
815     rightViewDimensions.size.width = viewDimensions.size.width - leftViewDimensions.size.width - f_dividerThickness;
816     rightViewDimensions.size.height = viewDimensions.size.height;
817     [[o_subviews objectAtIndex:1] setFrame: rightViewDimensions];
818 }
819 @end
820
821 /*****************************************************************************
822  * VLCThreePartImageView interface
823  *****************************************************************************/
824 @implementation VLCThreePartImageView
825 - (void)dealloc
826 {
827     [o_left_img release];
828     [o_middle_img release];
829     [o_right_img release];
830
831     [super dealloc];
832 }
833
834 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
835 {
836     if (o_left_img)
837         [o_left_img release];
838     if (o_middle_img)
839         [o_middle_img release];
840     if (o_right_img)
841         [o_right_img release];
842
843     o_left_img = [left retain];
844     o_middle_img = [middle retain];
845     o_right_img = [right retain];
846 }
847
848 - (void)drawRect:(NSRect)rect
849 {
850     NSRect bnds = [self bounds];
851     NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
852 }
853
854 @end