]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
macosx/CAS: add MKV to the black list for HTTP streaming
[vlc] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2012 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 "misc.h"
26 #import "intf.h"                                          /* VLCApplication */
27 #import "MainWindow.h"
28 #import "ControlsBar.h"
29 #import "controls.h"
30 #import "CoreInteraction.h"
31 #import <CoreAudio/CoreAudio.h>
32 #import <vlc_keys.h>
33
34
35 /*****************************************************************************
36  * NSSound (VLCAdditions)
37  *
38  * added code to change the system volume, needed for the apple remote code
39  * this is simplified code, which won't let you set the exact volume
40  * (that's what the audio output is for after all), but just the system volume
41  * in steps of 1/16 (matching the default AR or volume key implementation).
42  *****************************************************************************/
43
44 @implementation NSSound (VLCAdditions)
45
46 + (float)systemVolumeForChannel:(int)channel
47 {
48     AudioDeviceID i_device;
49     float f_volume;
50     OSStatus err;
51     UInt32 i_size;
52
53     i_size = sizeof( i_device );
54     AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
55     err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
56     if (err != noErr) {
57         msg_Warn( VLCIntf, "couldn't get main audio output device" );
58         return .0;
59     }
60
61     AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, channel };
62     i_size = sizeof( f_volume );
63     err = AudioObjectGetPropertyData(i_device, &propertyAddress, 0, NULL, &i_size, &f_volume);
64     if (err != noErr) {
65         msg_Warn( VLCIntf, "couldn't get volume value" );
66         return .0;
67     }
68
69     return f_volume;
70 }
71
72 + (bool)setSystemVolume:(float)f_volume forChannel:(int)i_channel
73 {
74     /* the following code will fail on S/PDIF devices. there is an easy work-around, but we'd like to match the OS behavior */
75
76     AudioDeviceID i_device;
77     OSStatus err;
78     UInt32 i_size;
79     Boolean b_writeable;
80
81     i_size = sizeof( i_device );
82     AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
83     err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
84     if (err != noErr) {
85         msg_Warn( VLCIntf, "couldn't get main audio output device" );
86         return NO;
87     }
88
89     AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, i_channel };
90     i_size = sizeof( f_volume );
91     err = AudioObjectIsPropertySettable( i_device, &propertyAddress, &b_writeable );
92     if (err != noErr || !b_writeable ) {
93         msg_Warn( VLCIntf, "we can't set the main audio devices' volume" );
94         return NO;
95     }
96     err = AudioObjectSetPropertyData(i_device, &propertyAddress, 0, NULL, i_size, &f_volume);
97
98     return YES;
99 }
100
101 + (void)increaseSystemVolume
102 {
103     float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
104     f_volume += .0625; // 1/16 to match the OS
105     bool b_returned = YES;
106
107     /* since core audio doesn't provide a reasonable way to see how many channels we got, let's see how long we can do this */
108     for (NSUInteger x = 1; b_returned ; x++)
109         b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
110 }
111
112 + (void)decreaseSystemVolume
113 {
114     float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
115     f_volume -= .0625; // 1/16 to match the OS
116     bool b_returned = YES;
117
118     /* since core audio doesn't provide a reasonable way to see how many channels we got, let's see how long we can do this */
119     for (NSUInteger x = 1; b_returned ; x++)
120         b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
121 }
122
123 @end
124
125 /*****************************************************************************
126  * NSAnimation (VLCAdditions)
127  *
128  *  Missing extension to NSAnimation
129  *****************************************************************************/
130
131 @implementation NSAnimation (VLCAdditions)
132 /* fake class attributes  */
133 static NSMapTable *VLCAdditions_userInfo = NULL;
134
135 + (void)load
136 {
137     /* init our fake object attribute */
138     VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
139 }
140
141 - (void)dealloc
142 {
143     NSMapRemove(VLCAdditions_userInfo, self);
144     [super dealloc];
145 }
146
147 - (void)setUserInfo: (void *)userInfo
148 {
149     NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
150 }
151
152 - (void *)userInfo
153 {
154     return NSMapGet(VLCAdditions_userInfo, self);
155 }
156 @end
157
158 /*****************************************************************************
159  * NSScreen (VLCAdditions)
160  *
161  *  Missing extension to NSScreen
162  *****************************************************************************/
163
164 @implementation NSScreen (VLCAdditions)
165
166 static NSMutableArray *blackoutWindows = NULL;
167
168 + (void)load
169 {
170     /* init our fake object attribute */
171     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
172 }
173
174 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
175 {
176     NSUInteger count = [[NSScreen screens] count];
177
178     for ( NSUInteger i = 0; i < count; i++ ) {
179         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
180         if ([screen displayID] == displayID)
181             return screen;
182     }
183     return nil;
184 }
185
186 - (BOOL)hasMenuBar
187 {
188     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
189 }
190
191 - (BOOL)hasDock
192 {
193     NSRect screen_frame = [self frame];
194     NSRect screen_visible_frame = [self visibleFrame];
195     CGFloat f_menu_bar_thickness = [self hasMenuBar] ? [[NSStatusBar systemStatusBar] thickness] : 0.0;
196
197     BOOL b_found_dock = NO;
198     if (screen_visible_frame.size.width < screen_frame.size.width)
199         b_found_dock = YES;
200     else if (screen_visible_frame.size.height + f_menu_bar_thickness < screen_frame.size.height)
201         b_found_dock = YES;
202
203     return b_found_dock;
204 }
205
206 - (BOOL)isScreen: (NSScreen*)screen
207 {
208     return ([self displayID] == [screen displayID]);
209 }
210
211 - (CGDirectDisplayID)displayID
212 {
213     return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
214 }
215
216 - (void)blackoutOtherScreens
217 {
218     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
219     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
220     [blackoutWindows removeAllObjects];
221
222     NSUInteger screenCount = [[NSScreen screens] count];
223     for (NSUInteger i = 0; i < screenCount; i++) {
224         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
225         VLCWindow *blackoutWindow;
226         NSRect screen_rect;
227
228         if ([self isScreen: screen])
229             continue;
230
231         screen_rect = [screen frame];
232         screen_rect.origin.x = screen_rect.origin.y = 0;
233
234         /* blackoutWindow alloc strategy
235             - The NSMutableArray blackoutWindows has the blackoutWindow references
236             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
237         */
238         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
239                 backing: NSBackingStoreBuffered defer: NO screen: screen];
240         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
241         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
242
243         [blackoutWindow displayIfNeeded];
244         [blackoutWindow orderFront: self animate: YES];
245
246         [blackoutWindows addObject: blackoutWindow];
247         [blackoutWindow release];
248
249         NSApplicationPresentationOptions presentationOpts = [NSApp presentationOptions];
250         if ([screen hasMenuBar])
251             presentationOpts |= NSApplicationPresentationAutoHideMenuBar;
252         if ([screen hasMenuBar] || [screen hasDock])
253             presentationOpts |= NSApplicationPresentationAutoHideDock;
254         [NSApp setPresentationOptions:presentationOpts];
255     }
256 }
257
258 + (void)unblackoutScreens
259 {
260     NSUInteger blackoutWindowCount = [blackoutWindows count];
261
262     for (NSUInteger i = 0; i < blackoutWindowCount; i++) {
263         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
264         [blackoutWindow closeAndAnimate: YES];
265     }
266
267     [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
268 }
269
270 @end
271
272 /*****************************************************************************
273  * VLBrushedMetalImageView
274  *****************************************************************************/
275
276 @implementation VLBrushedMetalImageView
277
278 - (BOOL)mouseDownCanMoveWindow
279 {
280     return YES;
281 }
282
283 - (void)dealloc
284 {
285     [self unregisterDraggedTypes];
286     [super dealloc];
287 }
288
289 - (void)awakeFromNib
290 {
291     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
292     [self setImageScaling: NSScaleToFit];
293     [self setImageFrameStyle: NSImageFrameNone];
294     [self setImageAlignment: NSImageAlignCenter];
295 }
296
297 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
298 {
299     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
300         return NSDragOperationGeneric;
301
302     return NSDragOperationNone;
303 }
304
305 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
306 {
307     return YES;
308 }
309
310 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
311 {
312     BOOL b_returned;
313     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
314
315     [self setNeedsDisplay:YES];
316     return b_returned;
317 }
318
319 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
320 {
321     [self setNeedsDisplay:YES];
322 }
323
324 @end
325
326
327 /*****************************************************************************
328  * MPSlider
329  *****************************************************************************/
330 @implementation MPSlider
331
332 void _drawKnobInRect(NSRect knobRect)
333 {
334     // Center knob in given rect
335     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
336     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
337
338     // Draw diamond
339     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
340     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
341     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
342     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
343     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
344     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
345     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
346 }
347
348 void _drawFrameInRect(NSRect frameRect)
349 {
350     // Draw frame
351     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
352     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
353     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
354     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
355 }
356
357 - (void)drawRect:(NSRect)rect
358 {
359     // Draw default to make sure the slider behaves correctly
360     [[NSGraphicsContext currentContext] saveGraphicsState];
361     NSRectClip(NSZeroRect);
362     [super drawRect:rect];
363     [[NSGraphicsContext currentContext] restoreGraphicsState];
364
365     // Full size
366     rect = [self bounds];
367     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
368     rect.origin.x += diff-1;
369     rect.origin.y += diff;
370     rect.size.width -= 2*diff-2;
371     rect.size.height -= 2*diff;
372
373     // Draw dark
374     NSRect knobRect = [[self cell] knobRectFlipped:NO];
375     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
376     _drawFrameInRect(rect);
377     _drawKnobInRect(knobRect);
378
379     // Draw shadow
380     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
381     rect.origin.x++;
382     rect.origin.y++;
383     knobRect.origin.x++;
384     knobRect.origin.y++;
385     _drawFrameInRect(rect);
386     _drawKnobInRect(knobRect);
387 }
388
389 @end
390
391 /*****************************************************************************
392  * ProgressView
393  *****************************************************************************/
394
395 @implementation VLCProgressView : NSView
396
397 - (void)scrollWheel:(NSEvent *)o_event
398 {
399     intf_thread_t * p_intf = VLCIntf;
400     CGFloat f_deltaY = [o_event deltaY];
401     CGFloat f_deltaX = [o_event deltaX];
402
403     if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
404         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
405     else
406         f_deltaY = -f_deltaY;
407
408     // positive for left / down, negative otherwise
409     CGFloat f_delta = f_deltaX + f_deltaY;
410     CGFloat f_abs;
411     int i_vlckey;
412
413     if (f_delta > 0.0f) {
414         i_vlckey = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
415         f_abs = f_delta;
416     }
417     else {
418         i_vlckey = ACTIONID_JUMP_FORWARD_EXTRASHORT;
419         f_abs = -f_delta;
420     }
421
422     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
423         var_SetInteger( p_intf->p_libvlc, "key-action", i_vlckey );
424 }
425
426 - (BOOL)acceptsFirstResponder
427 {
428     return YES;
429 }
430
431 @end
432
433 /*****************************************************************************
434  * TimeLineSlider
435  *****************************************************************************/
436
437 @implementation TimeLineSlider
438
439 - (void)awakeFromNib
440 {
441     if (config_GetInt( VLCIntf, "macosx-interfacestyle" )) {
442         o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
443         b_dark = YES;
444     } else {
445         o_knob_img = [NSImage imageNamed:@"progression-knob"];
446         b_dark = NO;
447     }
448     img_rect.size = [o_knob_img size];
449     img_rect.origin.x = img_rect.origin.y = 0;
450 }
451
452 - (void)dealloc
453 {
454     [o_knob_img release];
455     [super dealloc];
456 }
457
458 - (CGFloat)knobPosition
459 {
460     NSRect knobRect = [[self cell] knobRectFlipped:NO];
461     knobRect.origin.x += knobRect.size.width / 2;
462     return knobRect.origin.x;
463 }
464
465 - (void)drawKnobInRect:(NSRect)knobRect
466 {
467     knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
468     knobRect.size.width = img_rect.size.width;
469     knobRect.size.height = img_rect.size.height;
470     [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
471 }
472
473 - (void)drawRect:(NSRect)rect
474 {
475     [[[[VLCMain sharedInstance] mainWindow] controlsBar] drawFancyGradientEffectForTimeSlider];
476     msleep( 10000 ); //wait for the gradient to draw completely
477
478     /* Draw default to make sure the slider behaves correctly */
479     [[NSGraphicsContext currentContext] saveGraphicsState];
480     NSRectClip(NSZeroRect);
481     [super drawRect:rect];
482     [[NSGraphicsContext currentContext] restoreGraphicsState];
483
484     NSRect knobRect = [[self cell] knobRectFlipped:NO];
485     if (b_dark)
486         knobRect.origin.y+=2;
487     else
488         knobRect.origin.y+=1;
489     [self drawKnobInRect: knobRect];
490 }
491
492 @end
493
494 /*****************************************************************************
495  * VLCVolumeSliderCommon
496  *****************************************************************************/
497
498 @implementation VLCVolumeSliderCommon : NSSlider
499
500 - (void)scrollWheel:(NSEvent *)o_event
501 {
502     intf_thread_t * p_intf = VLCIntf;
503     CGFloat f_deltaY = [o_event deltaY];
504     CGFloat f_deltaX = [o_event deltaX];
505
506     if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
507         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
508     else
509         f_deltaY = -f_deltaY;
510
511     // positive for left / down, negative otherwise
512     CGFloat f_delta = f_deltaX + f_deltaY;
513     CGFloat f_abs;
514     int i_vlckey;
515
516     if (f_delta > 0.0f) {
517         i_vlckey = ACTIONID_VOL_DOWN;
518         f_abs = f_delta;
519     }
520     else {
521         i_vlckey = ACTIONID_VOL_UP;
522         f_abs = -f_delta;
523     }
524
525     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
526         var_SetInteger(p_intf->p_libvlc, "key-action", i_vlckey);
527 }
528
529 @end
530
531 /*****************************************************************************
532  * ITSlider
533  *****************************************************************************/
534
535 @implementation ITSlider
536
537 - (void)awakeFromNib
538 {
539     BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
540     if (b_dark)
541         img = [NSImage imageNamed:@"volume-slider-knob_dark"];
542     else
543         img = [NSImage imageNamed:@"volume-slider-knob"];
544
545     image_rect.size = [img size];
546     image_rect.origin.x = 0;
547
548     if (b_dark)
549         image_rect.origin.y = -1;
550     else
551         image_rect.origin.y = 0;
552 }
553
554 - (void)drawKnobInRect:(NSRect)knobRect
555 {
556     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
557     knobRect.size.width = image_rect.size.width;
558     knobRect.size.height = image_rect.size.height;
559     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
560 }
561
562 - (void)drawRect:(NSRect)rect
563 {
564     /* Draw default to make sure the slider behaves correctly */
565     [[NSGraphicsContext currentContext] saveGraphicsState];
566     NSRectClip(NSZeroRect);
567     [super drawRect:rect];
568     [[NSGraphicsContext currentContext] restoreGraphicsState];
569
570     NSRect knobRect = [[self cell] knobRectFlipped:NO];
571     knobRect.origin.y+=2;
572     [self drawKnobInRect: knobRect];
573 }
574
575 @end
576
577 /*****************************************************************************
578  * VLCTimeField implementation
579  *****************************************************************************
580  * we need this to catch our click-event in the controller window
581  *****************************************************************************/
582
583 @implementation VLCTimeField
584 + (void)initialize{
585     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
586     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
587
588     [defaults registerDefaults:appDefaults];
589 }
590
591 - (void)awakeFromNib
592 {
593     NSColor *o_string_color;
594     if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
595         o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
596     else
597         o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
598
599     textAlignment = NSCenterTextAlignment;
600     o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
601 }
602
603 - (void)setAlignment:(NSTextAlignment)alignment
604 {
605     textAlignment = alignment;
606     [self setStringValue:[self stringValue]];
607 }
608
609 - (void)dealloc
610 {
611     [o_string_shadow release];
612     [o_string_attributes_dict release];
613     [super dealloc];
614 }
615
616 - (void)setStringValue:(NSString *)string
617 {
618     if (!o_string_shadow) {
619         o_string_shadow = [[NSShadow alloc] init];
620         [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
621         [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.0)];
622         [o_string_shadow setShadowBlurRadius:0.0];
623     }
624
625     NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
626     NSUInteger i_stringLength = [string length];
627
628     [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
629     [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
630     [self setAttributedStringValue: o_attributed_string];
631     [o_attributed_string release];
632 }
633
634 - (void)mouseDown: (NSEvent *)ourEvent
635 {
636     if ( [ourEvent clickCount] > 1 )
637         [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
638     else
639     {
640         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
641             [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
642         else
643             [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
644     }
645 }
646
647 - (BOOL)timeRemaining
648 {
649     return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
650 }
651 @end
652
653 /*****************************************************************************
654  * VLCMainWindowSplitView implementation
655  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
656  *****************************************************************************/
657 @implementation VLCMainWindowSplitView : NSSplitView
658 /* 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.
659  */
660 - (NSColor *)dividerColor
661 {
662     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
663 }
664
665 /* 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.
666  */
667 - (CGFloat)dividerThickness
668 {
669     return 1.0;
670 }
671 @end
672
673 /*****************************************************************************
674  * VLCThreePartImageView interface
675  *****************************************************************************/
676 @implementation VLCThreePartImageView
677
678 - (void)dealloc
679 {
680     [o_left_img release];
681     [o_middle_img release];
682     [o_right_img release];
683
684     [super dealloc];
685 }
686
687 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
688 {
689     if (o_left_img)
690         [o_left_img release];
691     if (o_middle_img)
692         [o_middle_img release];
693     if (o_right_img)
694         [o_right_img release];
695
696     o_left_img = [left retain];
697     o_middle_img = [middle retain];
698     o_right_img = [right retain];
699 }
700
701 - (void)drawRect:(NSRect)rect
702 {
703     NSRect bnds = [self bounds];
704     NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
705 }
706
707 @end
708
709 @implementation VLCThreePartDropView
710
711 - (BOOL)mouseDownCanMoveWindow
712 {
713     return YES;
714 }
715
716 - (void)dealloc
717 {
718     [self unregisterDraggedTypes];
719     [super dealloc];
720 }
721
722 - (void)awakeFromNib
723 {
724     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
725 }
726
727 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
728 {
729     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
730         return NSDragOperationGeneric;
731
732     return NSDragOperationNone;
733 }
734
735 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
736 {
737     return YES;
738 }
739
740 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
741 {
742     BOOL b_returned;
743     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
744
745     [self setNeedsDisplay:YES];
746     return YES;
747 }
748
749 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
750 {
751     [self setNeedsDisplay:YES];
752 }
753
754 @end