]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
6c03a3b8869350059875c75893537d807b9da6db
[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)mainScreen
187 {
188     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
189 }
190
191 - (BOOL)isScreen: (NSScreen*)screen
192 {
193     return ([self displayID] == [screen displayID]);
194 }
195
196 - (CGDirectDisplayID)displayID
197 {
198     return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
199 }
200
201 - (void)blackoutOtherScreens
202 {
203     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
204     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
205     [blackoutWindows removeAllObjects];
206
207     NSUInteger screenCount = [[NSScreen screens] count];
208     for (NSUInteger i = 0; i < screenCount; i++) {
209         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
210         VLCWindow *blackoutWindow;
211         NSRect screen_rect;
212
213         if ([self isScreen: screen])
214             continue;
215
216         screen_rect = [screen frame];
217         screen_rect.origin.x = screen_rect.origin.y = 0;
218
219         /* blackoutWindow alloc strategy
220             - The NSMutableArray blackoutWindows has the blackoutWindow references
221             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
222         */
223         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
224                 backing: NSBackingStoreBuffered defer: NO screen: screen];
225         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
226         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
227
228         [blackoutWindow displayIfNeeded];
229         [blackoutWindow orderFront: self animate: YES];
230
231         [blackoutWindows addObject: blackoutWindow];
232         [blackoutWindow release];
233
234         if ( [screen mainScreen] )
235             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
236     }
237 }
238
239 + (void)unblackoutScreens
240 {
241     NSUInteger blackoutWindowCount = [blackoutWindows count];
242
243     for (NSUInteger i = 0; i < blackoutWindowCount; i++) {
244         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
245         [blackoutWindow closeAndAnimate: YES];
246     }
247
248     [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
249 }
250
251 @end
252
253 /*****************************************************************************
254  * VLBrushedMetalImageView
255  *****************************************************************************/
256
257 @implementation VLBrushedMetalImageView
258
259 - (BOOL)mouseDownCanMoveWindow
260 {
261     return YES;
262 }
263
264 - (void)dealloc
265 {
266     [self unregisterDraggedTypes];
267     [super dealloc];
268 }
269
270 - (void)awakeFromNib
271 {
272     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
273     [self setImageScaling: NSScaleToFit];
274     [self setImageFrameStyle: NSImageFrameNone];
275     [self setImageAlignment: NSImageAlignCenter];
276 }
277
278 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
279 {
280     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
281         return NSDragOperationGeneric;
282
283     return NSDragOperationNone;
284 }
285
286 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
287 {
288     return YES;
289 }
290
291 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
292 {
293     BOOL b_returned;
294     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
295
296     [self setNeedsDisplay:YES];
297     return b_returned;
298 }
299
300 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
301 {
302     [self setNeedsDisplay:YES];
303 }
304
305 @end
306
307
308 /*****************************************************************************
309  * MPSlider
310  *****************************************************************************/
311 @implementation MPSlider
312
313 void _drawKnobInRect(NSRect knobRect)
314 {
315     // Center knob in given rect
316     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
317     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
318
319     // Draw diamond
320     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
321     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
322     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
323     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
324     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
325     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
326     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
327 }
328
329 void _drawFrameInRect(NSRect frameRect)
330 {
331     // Draw frame
332     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
333     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
334     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
335     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
336 }
337
338 - (void)drawRect:(NSRect)rect
339 {
340     // Draw default to make sure the slider behaves correctly
341     [[NSGraphicsContext currentContext] saveGraphicsState];
342     NSRectClip(NSZeroRect);
343     [super drawRect:rect];
344     [[NSGraphicsContext currentContext] restoreGraphicsState];
345
346     // Full size
347     rect = [self bounds];
348     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
349     rect.origin.x += diff-1;
350     rect.origin.y += diff;
351     rect.size.width -= 2*diff-2;
352     rect.size.height -= 2*diff;
353
354     // Draw dark
355     NSRect knobRect = [[self cell] knobRectFlipped:NO];
356     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
357     _drawFrameInRect(rect);
358     _drawKnobInRect(knobRect);
359
360     // Draw shadow
361     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
362     rect.origin.x++;
363     rect.origin.y++;
364     knobRect.origin.x++;
365     knobRect.origin.y++;
366     _drawFrameInRect(rect);
367     _drawKnobInRect(knobRect);
368 }
369
370 @end
371
372 /*****************************************************************************
373  * ProgressView
374  *****************************************************************************/
375
376 @implementation VLCProgressView : NSView
377
378 - (void)scrollWheel:(NSEvent *)o_event
379 {
380     intf_thread_t * p_intf = VLCIntf;
381     CGFloat f_deltaY = [o_event deltaY];
382     CGFloat f_deltaX = [o_event deltaX];
383
384     if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
385         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
386     else
387         f_deltaY = -f_deltaY;
388
389     // positive for left / down, negative otherwise
390     CGFloat f_delta = f_deltaX + f_deltaY;
391     CGFloat f_abs;
392     int i_vlckey;
393
394     if (f_delta > 0.0f) {
395         i_vlckey = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
396         f_abs = f_delta;
397     }
398     else {
399         i_vlckey = ACTIONID_JUMP_FORWARD_EXTRASHORT;
400         f_abs = -f_delta;
401     }
402
403     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
404         var_SetInteger( p_intf->p_libvlc, "key-action", i_vlckey );
405 }
406
407 - (BOOL)acceptsFirstResponder
408 {
409     return YES;
410 }
411
412 @end
413
414 /*****************************************************************************
415  * TimeLineSlider
416  *****************************************************************************/
417
418 @implementation TimeLineSlider
419
420 - (void)awakeFromNib
421 {
422     if (config_GetInt( VLCIntf, "macosx-interfacestyle" )) {
423         o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
424         b_dark = YES;
425     } else {
426         o_knob_img = [NSImage imageNamed:@"progression-knob"];
427         b_dark = NO;
428     }
429     img_rect.size = [o_knob_img size];
430     img_rect.origin.x = img_rect.origin.y = 0;
431 }
432
433 - (void)dealloc
434 {
435     [o_knob_img release];
436     [super dealloc];
437 }
438
439 - (CGFloat)knobPosition
440 {
441     NSRect knobRect = [[self cell] knobRectFlipped:NO];
442     knobRect.origin.x += knobRect.size.width / 2;
443     return knobRect.origin.x;
444 }
445
446 - (void)drawKnobInRect:(NSRect)knobRect
447 {
448     knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
449     knobRect.size.width = img_rect.size.width;
450     knobRect.size.height = img_rect.size.height;
451     [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
452 }
453
454 - (void)drawRect:(NSRect)rect
455 {
456     [[[[VLCMain sharedInstance] mainWindow] controlsBar] drawFancyGradientEffectForTimeSlider];
457     msleep( 10000 ); //wait for the gradient to draw completely
458
459     /* Draw default to make sure the slider behaves correctly */
460     [[NSGraphicsContext currentContext] saveGraphicsState];
461     NSRectClip(NSZeroRect);
462     [super drawRect:rect];
463     [[NSGraphicsContext currentContext] restoreGraphicsState];
464
465     NSRect knobRect = [[self cell] knobRectFlipped:NO];
466     if (b_dark)
467         knobRect.origin.y+=2;
468     else
469         knobRect.origin.y+=1;
470     [self drawKnobInRect: knobRect];
471 }
472
473 @end
474
475 /*****************************************************************************
476  * VLCVolumeSliderCommon
477  *****************************************************************************/
478
479 @implementation VLCVolumeSliderCommon : NSSlider
480
481 - (void)scrollWheel:(NSEvent *)o_event
482 {
483     intf_thread_t * p_intf = VLCIntf;
484     CGFloat f_deltaY = [o_event deltaY];
485     CGFloat f_deltaX = [o_event deltaX];
486
487     if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
488         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
489     else
490         f_deltaY = -f_deltaY;
491
492     // positive for left / down, negative otherwise
493     CGFloat f_delta = f_deltaX + f_deltaY;
494     CGFloat f_abs;
495     int i_vlckey;
496
497     if (f_delta > 0.0f) {
498         i_vlckey = ACTIONID_VOL_DOWN;
499         f_abs = f_delta;
500     }
501     else {
502         i_vlckey = ACTIONID_VOL_UP;
503         f_abs = -f_delta;
504     }
505
506     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
507         var_SetInteger(p_intf->p_libvlc, "key-action", i_vlckey);
508 }
509
510 @end
511
512 /*****************************************************************************
513  * ITSlider
514  *****************************************************************************/
515
516 @implementation ITSlider
517
518 - (void)awakeFromNib
519 {
520     BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
521     if (b_dark)
522         img = [NSImage imageNamed:@"volume-slider-knob_dark"];
523     else
524         img = [NSImage imageNamed:@"volume-slider-knob"];
525
526     image_rect.size = [img size];
527     image_rect.origin.x = 0;
528
529     if (b_dark)
530         image_rect.origin.y = -1;
531     else
532         image_rect.origin.y = 0;
533 }
534
535 - (void)drawKnobInRect:(NSRect)knobRect
536 {
537     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
538     knobRect.size.width = image_rect.size.width;
539     knobRect.size.height = image_rect.size.height;
540     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
541 }
542
543 - (void)drawRect:(NSRect)rect
544 {
545     /* Draw default to make sure the slider behaves correctly */
546     [[NSGraphicsContext currentContext] saveGraphicsState];
547     NSRectClip(NSZeroRect);
548     [super drawRect:rect];
549     [[NSGraphicsContext currentContext] restoreGraphicsState];
550
551     NSRect knobRect = [[self cell] knobRectFlipped:NO];
552     knobRect.origin.y+=2;
553     [self drawKnobInRect: knobRect];
554 }
555
556 @end
557
558 /*****************************************************************************
559  * VLCTimeField implementation
560  *****************************************************************************
561  * we need this to catch our click-event in the controller window
562  *****************************************************************************/
563
564 @implementation VLCTimeField
565 + (void)initialize{
566     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
567     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
568
569     [defaults registerDefaults:appDefaults];
570 }
571
572 - (void)awakeFromNib
573 {
574     NSColor *o_string_color;
575     if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
576         o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
577     else
578         o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
579
580     textAlignment = NSCenterTextAlignment;
581     o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
582 }
583
584 - (void)setAlignment:(NSTextAlignment)alignment
585 {
586     textAlignment = alignment;
587     [self setStringValue:[self stringValue]];
588 }
589
590 - (void)dealloc
591 {
592     [o_string_shadow release];
593     [o_string_attributes_dict release];
594     [super dealloc];
595 }
596
597 - (void)setStringValue:(NSString *)string
598 {
599     if (!o_string_shadow) {
600         o_string_shadow = [[NSShadow alloc] init];
601         [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
602         [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.0)];
603         [o_string_shadow setShadowBlurRadius:0.0];
604     }
605
606     NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
607     NSUInteger i_stringLength = [string length];
608
609     [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
610     [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
611     [self setAttributedStringValue: o_attributed_string];
612     [o_attributed_string release];
613 }
614
615 - (void)mouseDown: (NSEvent *)ourEvent
616 {
617     if ( [ourEvent clickCount] > 1 )
618         [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
619     else
620     {
621         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
622             [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
623         else
624             [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
625     }
626 }
627
628 - (BOOL)timeRemaining
629 {
630     return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
631 }
632 @end
633
634 /*****************************************************************************
635  * VLCMainWindowSplitView implementation
636  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
637  *****************************************************************************/
638 @implementation VLCMainWindowSplitView : NSSplitView
639 /* 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.
640  */
641 - (NSColor *)dividerColor
642 {
643     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
644 }
645
646 /* 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.
647  */
648 - (CGFloat)dividerThickness
649 {
650     return 1.0;
651 }
652 @end
653
654 /*****************************************************************************
655  * VLCThreePartImageView interface
656  *****************************************************************************/
657 @implementation VLCThreePartImageView
658
659 - (void)dealloc
660 {
661     [o_left_img release];
662     [o_middle_img release];
663     [o_right_img release];
664
665     [super dealloc];
666 }
667
668 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
669 {
670     if (o_left_img)
671         [o_left_img release];
672     if (o_middle_img)
673         [o_middle_img release];
674     if (o_right_img)
675         [o_right_img release];
676
677     o_left_img = [left retain];
678     o_middle_img = [middle retain];
679     o_right_img = [right retain];
680 }
681
682 - (void)drawRect:(NSRect)rect
683 {
684     NSRect bnds = [self bounds];
685     NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
686 }
687
688 @end
689
690 @implementation VLCThreePartDropView
691
692 - (BOOL)mouseDownCanMoveWindow
693 {
694     return YES;
695 }
696
697 - (void)dealloc
698 {
699     [self unregisterDraggedTypes];
700     [super dealloc];
701 }
702
703 - (void)awakeFromNib
704 {
705     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
706 }
707
708 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
709 {
710     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
711         return NSDragOperationGeneric;
712
713     return NSDragOperationNone;
714 }
715
716 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
717 {
718     return YES;
719 }
720
721 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
722 {
723     BOOL b_returned;
724     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
725
726     [self setNeedsDisplay:YES];
727     return YES;
728 }
729
730 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
731 {
732     [self setNeedsDisplay:YES];
733 }
734
735 @end