]> git.sesse.net Git - vlc/commitdiff
vlc_app: New media layer view. Enabled by default. Set bindings from resources. ...
authorPierre d'Herbemont <pdherbemont@videolan.org>
Mon, 11 Feb 2008 00:02:42 +0000 (00:02 +0000)
committerPierre d'Herbemont <pdherbemont@videolan.org>
Mon, 11 Feb 2008 00:02:42 +0000 (00:02 +0000)
25 files changed:
projects/macosx/vlc_app/English.lproj/MainMenu.nib/classes.nib
projects/macosx/vlc_app/English.lproj/MainMenu.nib/info.nib
projects/macosx/vlc_app/English.lproj/MainMenu.nib/keyedobjects.nib
projects/macosx/vlc_app/Sources/AppleRemote.h [new file with mode: 0644]
projects/macosx/vlc_app/Sources/AppleRemote.m [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCAppAdditions.h
projects/macosx/vlc_app/Sources/VLCAppAdditions.m
projects/macosx/vlc_app/Sources/VLCBrowsableVideoView.h
projects/macosx/vlc_app/Sources/VLCBrowsableVideoView.m
projects/macosx/vlc_app/Sources/VLCController.m
projects/macosx/vlc_app/Sources/VLCExceptionHandler.m
projects/macosx/vlc_app/Sources/VLCFullScreenControllerWindow.h
projects/macosx/vlc_app/Sources/VLCFullScreenControllerWindow.m
projects/macosx/vlc_app/Sources/VLCMainWindow.h
projects/macosx/vlc_app/Sources/VLCMainWindow.m
projects/macosx/vlc_app/Sources/VLCMainWindowController.h [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCMainWindowController.m [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCMediaArrayController.m
projects/macosx/vlc_app/Sources/VLCMediaLayer.h [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCMediaLayer.m [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCMediaListLayer.h [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCMediaListLayer.m [new file with mode: 0644]
projects/macosx/vlc_app/Sources/VLCValueTransformer.h
projects/macosx/vlc_app/Sources/VLCValueTransformer.m
projects/macosx/vlc_app/VLC.xcodeproj/project.pbxproj

index b0470a8ff25b96423cac8396ac7c6109dfddeeb9..ecd45e7ab422032828a60afa4edace0e8ba8fd03 100644 (file)
                        <key>SUPERCLASS</key>
                        <string>NSObject</string>
                </dict>
+               <dict>
+                       <key>ACTIONS</key>
+                       <dict>
+                               <key>mediaListViewItemDoubleClicked</key>
+                               <string>id</string>
+                               <key>videoViewItemClicked</key>
+                               <string>id</string>
+                       </dict>
+                       <key>CLASS</key>
+                       <string>VLCMainWindowController</string>
+                       <key>LANGUAGE</key>
+                       <string>ObjC</string>
+                       <key>OUTLETS</key>
+                       <dict>
+                               <key>addPlaylistButton</key>
+                               <string>NSButton</string>
+                               <key>categoriesListView</key>
+                               <string>NSOutlineView</string>
+                               <key>categoriesTreeController</key>
+                               <string>NSTreeController</string>
+                               <key>controller</key>
+                               <string>VLCController</string>
+                               <key>mainSplitView</key>
+                               <string>VLCOneSplitView</string>
+                               <key>mediaArrayController</key>
+                               <string>VLCMediaArrayController</string>
+                               <key>mediaListView</key>
+                               <string>NSTableView</string>
+                               <key>mediaPlayer</key>
+                               <string>VLCMediaPlayer</string>
+                               <key>mediaPlayerBackwardPrevButton</key>
+                               <string>NSButton</string>
+                               <key>mediaPlayerForwardNextButton</key>
+                               <string>NSButton</string>
+                               <key>mediaPlayerPlayPauseStopButton</key>
+                               <string>NSButton</string>
+                               <key>removePlaylistButton</key>
+                               <string>NSButton</string>
+                               <key>toolbarMediaAudioVolume</key>
+                               <string>NSView</string>
+                               <key>toolbarMediaControl</key>
+                               <string>NSView</string>
+                               <key>toolbarMediaDescription</key>
+                               <string>NSView</string>
+                               <key>videoView</key>
+                               <string>VLCBrowsableVideoView</string>
+                       </dict>
+                       <key>SUPERCLASS</key>
+                       <string>NSWindowController</string>
+               </dict>
                <dict>
                        <key>CLASS</key>
                        <string>FirstResponder</string>
                        <key>SUPERCLASS</key>
                        <string>NSObject</string>
                </dict>
+               <dict>
+                       <key>CLASS</key>
+                       <string>VLCOneSplitView</string>
+                       <key>LANGUAGE</key>
+                       <string>ObjC</string>
+                       <key>SUPERCLASS</key>
+                       <string>NSSplitView</string>
+               </dict>
+               <dict>
+                       <key>CLASS</key>
+                       <string>VLCMediaArrayController</string>
+                       <key>LANGUAGE</key>
+                       <string>ObjC</string>
+                       <key>SUPERCLASS</key>
+                       <string>NSArrayController</string>
+               </dict>
                <dict>
                        <key>ACTIONS</key>
                        <dict>
                        <string>ObjC</string>
                        <key>OUTLETS</key>
                        <dict>
+                               <key>mainWindowController</key>
+                               <string>VLCMainWindowController</string>
                                <key>selectedObject</key>
                                <string>id</string>
                                <key>target</key>
                        <key>SUPERCLASS</key>
                        <string>NSView</string>
                </dict>
+               <dict>
+                       <key>CLASS</key>
+                       <string>VLCMediaPlayer</string>
+                       <key>LANGUAGE</key>
+                       <string>ObjC</string>
+               </dict>
        </array>
        <key>IBVersion</key>
        <string>1</string>
index f6047775ae24c7956fcacc991d34bbce086c16dc..a2db77c154beccc5a05b6cb2172de930ea7124fa 100644 (file)
@@ -4,8 +4,6 @@
 <dict>
        <key>IBFramework Version</key>
        <string>629</string>
-       <key>IBLastKnownRelativeProjectPath</key>
-       <string>../../VLC.xcodeproj</string>
        <key>IBOldestOS</key>
        <integer>5</integer>
        <key>IBOpenObjects</key>
index 916037ddd22d95c652c9f304978ffc3007fd1a1a..b55f85ae402ad14b2416ff344749593f89fb215b 100644 (file)
Binary files a/projects/macosx/vlc_app/English.lproj/MainMenu.nib/keyedobjects.nib and b/projects/macosx/vlc_app/English.lproj/MainMenu.nib/keyedobjects.nib differ
diff --git a/projects/macosx/vlc_app/Sources/AppleRemote.h b/projects/macosx/vlc_app/Sources/AppleRemote.h
new file mode 100644 (file)
index 0000000..17a0aed
--- /dev/null
@@ -0,0 +1,199 @@
+/*****************************************************************************
+ * AppleRemote.h
+ * AppleRemote
+ * $Id: AppleRemote.h 22000 2007-09-13 19:12:49Z funman $
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************
+ *
+ * Note that changes made by any members or contributors of the VideoLAN team
+ * (i.e. changes that were checked in exclusively into one of VideoLAN's source code
+ * repositories) are licensed under the GNU General Public License version 2,
+ * or (at your option) any later version.
+ * Thus, the following statements apply to our changes:
+ *
+ * Copyright (C) 2006-2007 the VideoLAN team
+ * Authors: Eric Petit <titer@m0k.org>
+ *          Felix Kühne <fkuehne at videolan dot org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDLib.h>
+#import <IOKit/hid/IOHIDKeys.h>
+
+enum AppleRemoteEventIdentifier
+{
+    kRemoteButtonVolume_Plus        =1<<1,
+    kRemoteButtonVolume_Minus       =1<<2,
+    kRemoteButtonMenu               =1<<3,
+    kRemoteButtonPlay               =1<<4,
+    kRemoteButtonRight              =1<<5,
+    kRemoteButtonLeft               =1<<6,
+    kRemoteButtonRight_Hold         =1<<7,
+    kRemoteButtonLeft_Hold          =1<<8,
+    kRemoteButtonMenu_Hold          =1<<9,
+    kRemoteButtonPlay_Sleep         =1<<10,
+    kRemoteControl_Switched         =1<<11,
+    kRemoteButtonVolume_Plus_Hold   =1<<12,
+    kRemoteButtonVolume_Minus_Hold  =1<<13
+};
+typedef enum AppleRemoteEventIdentifier AppleRemoteEventIdentifier;
+
+/*  Encapsulates usage of the apple remote control
+This class is implemented as a singleton as there is exactly one remote per machine (until now)
+The class is not thread safe
+*/
+@interface AppleRemote : NSObject {
+    IOHIDDeviceInterface** hidDeviceInterface;
+    IOHIDQueueInterface**  queue;
+    NSMutableArray*        allCookies;
+    NSMutableDictionary*   cookieToButtonMapping;
+
+    BOOL openInExclusiveMode;
+    BOOL simulatePlusMinusHold;
+    BOOL processesBacklog;
+
+    /* state for simulating plus/minus hold */
+    BOOL lastEventSimulatedHold;
+    AppleRemoteEventIdentifier lastPlusMinusEvent;
+    NSTimeInterval lastPlusMinusEventTime;
+
+    int remoteId;
+    unsigned int clickCountEnabledButtons;
+    NSTimeInterval maxClickTimeDifference;
+    NSTimeInterval lastClickCountEventTime;
+    AppleRemoteEventIdentifier lastClickCountEvent;
+    unsigned int eventClickCount;
+
+    IBOutlet id delegate;
+}
+
+- (int) remoteId;
+
+- (BOOL) isRemoteAvailable;
+
+- (BOOL) isListeningToRemote;
+- (void) setListeningToRemote: (BOOL) value;
+
+- (BOOL) isOpenInExclusiveMode;
+- (void) setOpenInExclusiveMode: (BOOL) value;
+
+/* click counting makes it possible to recognize if the user has pressed a button repeatedly
+ * click counting does delay each event as it has to wait if there is another event (second click)
+ * therefore there is a slight time difference (maximumClickCountTimeDifference) between a single click
+ * of the user and the call of your delegate method
+ * click counting can be enabled individually for specific buttons. Use the property clickCountEnableButtons
+ * to set the buttons for which click counting shall be enabled */
+- (BOOL) clickCountingEnabled;
+- (void) setClickCountingEnabled: (BOOL) value;
+
+- (unsigned int) clickCountEnabledButtons;
+- (void) setClickCountEnabledButtons: (unsigned int)value;
+
+/* the maximum time difference till which clicks are recognized as multi clicks */
+- (NSTimeInterval) maximumClickCountTimeDifference;
+- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff;
+
+/* When your application needs to much time on the main thread when processing an event other events
+ * may already be received which are put on a backlog. As soon as your main thread
+ * has some spare time this backlog is processed and may flood your delegate with calls.
+ * Backlog processing is turned off by default. */
+- (BOOL) processesBacklog;
+- (void) setProcessesBacklog: (BOOL) value;
+
+/* Sets an NSApplication delegate which starts listening when application is becoming active
+ * and stops listening when application resigns being active.
+ * If an NSApplication delegate has been already set all method calls will be forwarded to this delegate, too. */
+- (BOOL) listeningOnAppActivate;
+- (void) setListeningOnAppActivate: (BOOL) value;
+
+/* Simulating plus/minus hold does deactivate sending of individual requests for plus/minus pressed down/released.
+ * Instead special hold events are being triggered when the user is pressing and holding plus/minus for a small period.
+ * With simulating enabled the plus/minus buttons do behave as the left/right buttons */
+- (BOOL) simulatesPlusMinusHold;
+- (void) setSimulatesPlusMinusHold: (BOOL) value;
+
+/* Delegates are not retained */
+- (void) setDelegate: (id) delegate;
+- (id) delegate;
+
+- (IBAction) startListening: (id) sender;
+- (IBAction) stopListening: (id) sender;
+@end
+
+@interface AppleRemote (Singleton)
+
++ (AppleRemote*) sharedRemote;
+
+@end
+
+/*  Method definitions for the delegate of the AppleRemote class */
+@interface NSObject(NSAppleRemoteDelegate)
+
+- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown clickCount: (unsigned int) count;
+
+@end
+
+@interface AppleRemote (PrivateMethods)
+- (void) setRemoteId: (int) aValue;
+- (NSDictionary*) cookieToButtonMapping;
+- (IOHIDQueueInterface**) queue;
+- (IOHIDDeviceInterface**) hidDeviceInterface;
+- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues;
+@end
+
+@interface AppleRemote (IOKitMethods)
+- (io_object_t) findAppleRemoteDevice;
+- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice;
+- (BOOL) initializeCookies;
+- (BOOL) openDevice;
+@end
+
+/* A NSApplication delegate which is used to activate and deactivate listening to the remote control
+ * dependent on the activation state of your application.
+ * All events are delegated to the original NSApplication delegate if necessary */
+@interface AppleRemoteApplicationDelegate : NSObject {
+    id applicationDelegate;
+}
+
+- (id) initWithApplicationDelegate: (id) delegate;
+- (id) applicationDelegate;
+@end
diff --git a/projects/macosx/vlc_app/Sources/AppleRemote.m b/projects/macosx/vlc_app/Sources/AppleRemote.m
new file mode 100644 (file)
index 0000000..bbe556c
--- /dev/null
@@ -0,0 +1,726 @@
+/*****************************************************************************
+ * AppleRemote.m
+ * AppleRemote
+ * $Id: AppleRemote.m 23523 2007-12-10 00:35:23Z fkuehne $
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************
+ *
+ * Note that changes made by any members or contributors of the VideoLAN team
+ * (i.e. changes that were exclusively checked in to one of VideoLAN's source code
+ * repositories) are licensed under the GNU General Public License version 2,
+ * or (at your option) any later version.
+ * Thus, the following statements apply to our changes:
+ *
+ * Copyright (C) 2006-2007 the VideoLAN team
+ * Authors: Eric Petit <titer@m0k.org>
+ *          Felix Kühne <fkuehne at videolan dot org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "AppleRemote.h"
+
+#define MACOS_VERSION [[[NSDictionary dictionaryWithContentsOfFile: \
+            @"/System/Library/CoreServices/SystemVersion.plist"] \
+            objectForKey: @"ProductVersion"] floatValue]
+
+const char* AppleRemoteDeviceName = "AppleIRController";
+const int REMOTE_SWITCH_COOKIE=19;
+const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE=0.35;
+const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL=0.4;
+
+@implementation AppleRemote
+
+#pragma public interface
+
+- (id) init {
+    if ( self = [super init] ) {
+        openInExclusiveMode = YES;
+        queue = NULL;
+        hidDeviceInterface = NULL;
+        cookieToButtonMapping = [[NSMutableDictionary alloc] init];
+
+        if( MACOS_VERSION < 10.5f )
+        {
+            /* use the traditional cookies for Tiger (and Panther, if it is supported by the frame app) */
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus]  forKey:@"14_12_11_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus] forKey:@"14_13_11_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]         forKey:@"14_7_6_14_7_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]         forKey:@"14_8_6_14_8_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]        forKey:@"14_9_6_14_9_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]         forKey:@"14_10_6_14_10_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold]   forKey:@"14_6_4_2_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold]    forKey:@"14_6_3_2_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold]    forKey:@"14_6_14_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep]   forKey:@"18_14_6_18_14_6_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]   forKey:@"19_"];
+        }
+        else
+        {
+            /* we're on Leopard and need to use a new set of cookies */
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus]  forKey:@"31_29_28_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus] forKey:@"31_30_28_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]         forKey:@"31_20_18_31_20_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]         forKey:@"31_21_18_31_21_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]        forKey:@"31_22_18_31_22_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]         forKey:@"31_23_18_31_23_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold]   forKey:@"31_18_4_2_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold]    forKey:@"31_18_3_2_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold]    forKey:@"31_18_31_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep]   forKey:@"35_31_18_35_31_18_"];
+            [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]   forKey:@"19_"];
+        }
+
+        /* defaults */
+        [self setSimulatesPlusMinusHold: YES];
+        maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
+    }
+
+    return self;
+}
+
+- (void) dealloc {
+    [self stopListening:self];
+    [cookieToButtonMapping release];
+    [super dealloc];
+}
+
+- (int) remoteId {
+    return remoteId;
+}
+
+- (BOOL) isRemoteAvailable {
+    io_object_t hidDevice = [self findAppleRemoteDevice];
+    if (hidDevice != 0) {
+        IOObjectRelease(hidDevice);
+        return YES;
+    } else {
+        return NO;
+    }
+}
+
+- (BOOL) isListeningToRemote {
+    return (hidDeviceInterface != NULL && allCookies != NULL && queue != NULL);
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+    if (value == NO) {
+        [self stopListening:self];
+    } else {
+        [self startListening:self];
+    }
+}
+
+/* Delegates are not retained!
+ * http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
+ * Delegating objects do not (and should not) retain their delegates.
+ * However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around
+ * to receive delegation messages. To do this, they may have to retain the delegate. */
+- (void) setDelegate: (id) _delegate {
+    if (_delegate && [_delegate respondsToSelector:@selector(appleRemoteButton:pressedDown:clickCount:)]==NO) return;
+
+    delegate = _delegate;
+}
+- (id) delegate {
+    return delegate;
+}
+
+- (BOOL) isOpenInExclusiveMode {
+    return openInExclusiveMode;
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+    openInExclusiveMode = value;
+}
+
+- (BOOL) clickCountingEnabled {
+    return clickCountEnabledButtons != 0;
+}
+- (void) setClickCountingEnabled: (BOOL) value {
+    if (value) {
+        [self setClickCountEnabledButtons: kRemoteButtonVolume_Plus | kRemoteButtonVolume_Minus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu];
+    } else {
+        [self setClickCountEnabledButtons: 0];
+    }
+}
+
+- (unsigned int) clickCountEnabledButtons {
+    return clickCountEnabledButtons;
+}
+- (void) setClickCountEnabledButtons: (unsigned int)value {
+    clickCountEnabledButtons = value;
+}
+
+- (NSTimeInterval) maximumClickCountTimeDifference {
+    return maxClickTimeDifference;
+}
+- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff {
+    maxClickTimeDifference = timeDiff;
+}
+
+- (BOOL) processesBacklog {
+    return processesBacklog;
+}
+- (void) setProcessesBacklog: (BOOL) value {
+    processesBacklog = value;
+}
+
+- (BOOL) listeningOnAppActivate {
+    id appDelegate = [NSApp delegate];
+    return (appDelegate!=nil && [appDelegate isKindOfClass: [AppleRemoteApplicationDelegate class]]);
+}
+- (void) setListeningOnAppActivate: (BOOL) value {
+    if (value) {
+        if ([self listeningOnAppActivate]) return;
+        AppleRemoteApplicationDelegate* appDelegate = [[AppleRemoteApplicationDelegate alloc] initWithApplicationDelegate: [NSApp delegate]];
+        /* NSApp does not retain its delegate therefore we keep retain count on 1 */
+        [NSApp setDelegate: appDelegate];
+    } else {
+        if ([self listeningOnAppActivate]==NO) return;
+        AppleRemoteApplicationDelegate* appDelegate = (AppleRemoteApplicationDelegate*)[NSApp delegate];
+        id previousAppDelegate = [appDelegate applicationDelegate];
+        [NSApp setDelegate: previousAppDelegate];
+        [appDelegate release];
+    }
+}
+
+- (BOOL) simulatesPlusMinusHold {
+    return simulatePlusMinusHold;
+}
+- (void) setSimulatesPlusMinusHold: (BOOL) value {
+    simulatePlusMinusHold = value;
+}
+
+- (IBAction) startListening: (id) sender {
+    if ([self isListeningToRemote]) return;
+
+    io_object_t hidDevice = [self findAppleRemoteDevice];
+    if (hidDevice == 0) return;
+
+    if ([self createInterfaceForDevice:hidDevice] == NULL) {
+        goto error;
+    }
+
+    if ([self initializeCookies]==NO) {
+        goto error;
+    }
+
+    if ([self openDevice]==NO) {
+        goto error;
+    }
+    goto cleanup;
+
+error:
+    [self stopListening:self];
+
+cleanup:
+    IOObjectRelease(hidDevice);
+}
+
+- (IBAction) stopListening: (id) sender {
+    if (queue != NULL) {
+        (*queue)->stop(queue);
+
+        //dispose of queue
+        (*queue)->dispose(queue);
+
+        //release the queue we allocated
+        (*queue)->Release(queue);
+
+        queue = NULL;
+    }
+
+    if (allCookies != nil) {
+        [allCookies autorelease];
+        allCookies = nil;
+    }
+
+    if (hidDeviceInterface != NULL) {
+        //close the device
+        (*hidDeviceInterface)->close(hidDeviceInterface);
+
+        //release the interface
+        (*hidDeviceInterface)->Release(hidDeviceInterface);
+
+        hidDeviceInterface = NULL;
+    }
+}
+
+@end
+
+@implementation AppleRemote (Singleton)
+
+static AppleRemote* sharedInstance=nil;
+
++ (AppleRemote*) sharedRemote {
+    @synchronized(self) {
+        if (sharedInstance == nil) {
+            sharedInstance = [[self alloc] init];
+        }
+    }
+    return sharedInstance;
+}
++ (id)allocWithZone:(NSZone *)zone {
+    @synchronized(self) {
+        if (sharedInstance == nil) {
+            return [super allocWithZone:zone];
+        }
+    }
+    return sharedInstance;
+}
+- (id)copyWithZone:(NSZone *)zone {
+    return self;
+}
+- (id)retain {
+    return self;
+}
+- (unsigned)retainCount {
+    return UINT_MAX;  //denotes an object that cannot be released
+}
+- (void)release {
+    //do nothing
+}
+- (id)autorelease {
+    return self;
+}
+
+@end
+
+@implementation AppleRemote (PrivateMethods)
+
+- (void) setRemoteId: (int) value {
+    remoteId = value;
+}
+
+- (IOHIDQueueInterface**) queue {
+    return queue;
+}
+
+- (IOHIDDeviceInterface**) hidDeviceInterface {
+    return hidDeviceInterface;
+}
+
+
+- (NSDictionary*) cookieToButtonMapping {
+    return cookieToButtonMapping;
+}
+
+- (NSString*) validCookieSubstring: (NSString*) cookieString {
+    if (cookieString == nil || [cookieString length] == 0) return nil;
+    NSEnumerator* keyEnum = [[self cookieToButtonMapping] keyEnumerator];
+    NSString* key;
+    while(key = [keyEnum nextObject]) {
+        NSRange range = [cookieString rangeOfString:key];
+        if (range.location == 0) return key;
+    }
+    return nil;
+}
+
+- (void) sendSimulatedPlusMinusEvent: (id) time {
+    BOOL startSimulateHold = NO;
+    AppleRemoteEventIdentifier event = lastPlusMinusEvent;
+    @synchronized(self) {
+        startSimulateHold = (lastPlusMinusEvent>0 && lastPlusMinusEventTime == [time doubleValue]);
+    }
+    if (startSimulateHold) {
+        lastEventSimulatedHold = YES;
+        event = (event==kRemoteButtonVolume_Plus) ? kRemoteButtonVolume_Plus_Hold : kRemoteButtonVolume_Minus_Hold;
+        [delegate appleRemoteButton:event pressedDown: YES clickCount: 1];
+    }
+}
+
+- (void) sendRemoteButtonEvent: (AppleRemoteEventIdentifier) event pressedDown: (BOOL) pressedDown {
+    if (delegate) {
+        if (simulatePlusMinusHold) {
+            if (event == kRemoteButtonVolume_Plus || event == kRemoteButtonVolume_Minus) {
+                if (pressedDown) {
+                    lastPlusMinusEvent = event;
+                    lastPlusMinusEventTime = [NSDate timeIntervalSinceReferenceDate];
+                    [self performSelector:@selector(sendSimulatedPlusMinusEvent:)
+                               withObject:[NSNumber numberWithDouble:lastPlusMinusEventTime]
+                               afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
+                    return;
+                } else {
+                    if (lastEventSimulatedHold) {
+                        event = (event==kRemoteButtonVolume_Plus) ? kRemoteButtonVolume_Plus_Hold : kRemoteButtonVolume_Minus_Hold;
+                        lastPlusMinusEvent = 0;
+                        lastEventSimulatedHold = NO;
+                    } else {
+                        @synchronized(self) {
+                            lastPlusMinusEvent = 0;
+                        }
+                        pressedDown = YES;
+                    }
+                }
+            }
+        }
+
+        if (([self clickCountEnabledButtons] & event) == event) {
+            if (pressedDown==NO && (event == kRemoteButtonVolume_Minus || event == kRemoteButtonVolume_Plus)) {
+                return; // this one is triggered automatically by the handler
+            }
+            NSNumber* eventNumber;
+            NSNumber* timeNumber;
+            @synchronized(self) {
+                lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate];
+                if (lastClickCountEvent == event) {
+                    eventClickCount = eventClickCount + 1;
+                } else {
+                    eventClickCount = 1;
+                }
+                lastClickCountEvent = event;
+                timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
+                eventNumber= [NSNumber numberWithUnsignedInt:event];
+            }
+            [self performSelector: @selector(executeClickCountEvent:)
+                       withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+                       afterDelay: maxClickTimeDifference];
+        } else {
+            [delegate appleRemoteButton:event pressedDown: pressedDown clickCount:1];
+        }
+    }
+}
+
+- (void) executeClickCountEvent: (NSArray*) values {
+    AppleRemoteEventIdentifier event = [[values objectAtIndex: 0] unsignedIntValue];
+    NSTimeInterval eventTimePoint = [[values objectAtIndex: 1] doubleValue];
+
+    BOOL finishedClicking = NO;
+    int finalClickCount = eventClickCount;
+
+    @synchronized(self) {
+        finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime);
+        if (finishedClicking) eventClickCount = 0;
+    }
+
+    if (finishedClicking) {
+        [delegate appleRemoteButton:event pressedDown: YES clickCount:finalClickCount];
+        if ([self simulatesPlusMinusHold]==NO && (event == kRemoteButtonVolume_Minus || event == kRemoteButtonVolume_Plus)) {
+            // trigger a button release event, too
+            [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
+            [delegate appleRemoteButton:event pressedDown: NO clickCount:finalClickCount];
+        }
+    }
+
+}
+
+- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues {
+    /*
+    if (previousRemainingCookieString) {
+        cookieString = [previousRemainingCookieString stringByAppendingString: cookieString];
+        NSLog(@"New cookie string is %@", cookieString);
+        [previousRemainingCookieString release], previousRemainingCookieString=nil;
+    }*/
+    if (cookieString == nil || [cookieString length] == 0) return;
+    NSNumber* buttonId = [[self cookieToButtonMapping] objectForKey: cookieString];
+    if (buttonId != nil) {
+        [self sendRemoteButtonEvent: [buttonId intValue] pressedDown: (sumOfValues>0)];
+    } else {
+        // let's see if a number of events are stored in the cookie string. this does
+        // happen when the main thread is too busy to handle all incoming events in time.
+        NSString* subCookieString;
+        NSString* lastSubCookieString=nil;
+        while(subCookieString = [self validCookieSubstring: cookieString]) {
+            cookieString = [cookieString substringFromIndex: [subCookieString length]];
+            lastSubCookieString = subCookieString;
+            if (processesBacklog) [self handleEventWithCookieString: subCookieString sumOfValues:sumOfValues];
+        }
+        if (processesBacklog == NO && lastSubCookieString != nil) {
+            // process the last event of the backlog and assume that the button is not pressed down any longer.
+            // The events in the backlog do not seem to be in order and therefore (in rare cases) the last event might be
+            // a button pressed down event while in reality the user has released it.
+            // NSLog(@"processing last event of backlog");
+            [self handleEventWithCookieString: lastSubCookieString sumOfValues:0];
+        }
+        if ([cookieString length] > 0) {
+            NSLog(@"Unknown button for cookiestring %@", cookieString);
+        }
+    }
+}
+
+@end
+
+/*  Callback method for the device queue
+Will be called for any event of any type (cookie) to which we subscribe
+*/
+static void QueueCallbackFunction(void* target,  IOReturn result, void* refcon, void* sender) {
+    AppleRemote* remote = (AppleRemote*)target;
+
+    IOHIDEventStruct event;
+    AbsoluteTime     zeroTime = {0,0};
+    NSMutableString* cookieString = [NSMutableString string];
+    SInt32           sumOfValues = 0;
+    while (result == kIOReturnSuccess)
+    {
+        result = (*[remote queue])->getNextEvent([remote queue], &event, zeroTime, 0);
+        if ( result != kIOReturnSuccess )
+            continue;
+
+        //printf("%d %d %d\n", event.elementCookie, event.value, event.longValue);
+
+        if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie) {
+            [remote setRemoteId: event.value];
+            [remote handleEventWithCookieString: @"19_" sumOfValues: 0];
+        } else {
+            if (((int)event.elementCookie)!=5) {
+                sumOfValues+=event.value;
+                [cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]];
+            }
+        }
+    }
+
+    [remote handleEventWithCookieString: cookieString sumOfValues: sumOfValues];
+}
+
+@implementation AppleRemote (IOKitMethods)
+
+- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice {
+    io_name_t               className;
+    IOCFPlugInInterface**   plugInInterface = NULL;
+    HRESULT                 plugInResult = S_OK;
+    SInt32                  score = 0;
+    IOReturn                ioReturnValue = kIOReturnSuccess;
+
+    hidDeviceInterface = NULL;
+
+    ioReturnValue = IOObjectGetClass(hidDevice, className);
+
+    if (ioReturnValue != kIOReturnSuccess) {
+        NSLog(@"Error: Failed to get class name.");
+        return NULL;
+    }
+
+    ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice,
+                                                      kIOHIDDeviceUserClientTypeID,
+                                                      kIOCFPlugInInterfaceID,
+                                                      &plugInInterface,
+                                                      &score);
+    if (ioReturnValue == kIOReturnSuccess)
+    {
+        //Call a method of the intermediate plug-in to create the device interface
+        plugInResult = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &hidDeviceInterface);
+
+        if (plugInResult != S_OK) {
+            NSLog(@"Error: Couldn't create HID class device interface");
+        }
+        // Release
+        if (plugInInterface) (*plugInInterface)->Release(plugInInterface);
+    }
+    return hidDeviceInterface;
+}
+
+- (io_object_t) findAppleRemoteDevice {
+    CFMutableDictionaryRef hidMatchDictionary = NULL;
+    IOReturn ioReturnValue = kIOReturnSuccess;
+    io_iterator_t hidObjectIterator = 0;
+    io_object_t hidDevice = 0;
+
+    // Set up a matching dictionary to search the I/O Registry by class
+    // name for all HID class devices
+    hidMatchDictionary = IOServiceMatching(AppleRemoteDeviceName);
+
+    // Now search I/O Registry for matching devices.
+    ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
+
+    if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
+        hidDevice = IOIteratorNext(hidObjectIterator);
+    }
+
+    // release the iterator
+    IOObjectRelease(hidObjectIterator);
+
+    return hidDevice;
+}
+
+- (BOOL) initializeCookies {
+    IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
+    IOHIDElementCookie      cookie;
+    long                    usage;
+    long                    usagePage;
+    id                      object;
+    NSArray*                elements = nil;
+    NSDictionary*           element;
+    IOReturn success;
+
+    if (!handle || !(*handle)) return NO;
+
+    /* Copy all elements, since we're grabbing most of the elements
+     * for this device anyway, and thus, it's faster to iterate them
+     * ourselves. When grabbing only one or two elements, a matching
+     * dictionary should be passed in here instead of NULL. */
+    success = (*handle)->copyMatchingElements(handle, NULL, (CFArrayRef*)&elements);
+
+    if (success == kIOReturnSuccess) {
+
+        [elements autorelease];
+        /*
+        cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie));
+        memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS);
+        */
+        allCookies = [[NSMutableArray alloc] init];
+        int i;
+        for (i=0; i< [elements count]; i++) {
+            element = [elements objectAtIndex:i];
+
+            //Get cookie
+            object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementCookieKey) ];
+            if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
+            if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
+            cookie = (IOHIDElementCookie) [object longValue];
+
+            //Get usage
+            object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsageKey) ];
+            if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
+            usage = [object longValue];
+
+            //Get usage page
+            object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsagePageKey) ];
+            if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
+            usagePage = [object longValue];
+
+            [allCookies addObject: [NSNumber numberWithInt:(int)cookie]];
+        }
+    } else {
+        return NO;
+    }
+
+    return YES;
+}
+
+- (BOOL) openDevice {
+    HRESULT  result;
+
+    IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
+    if ([self isOpenInExclusiveMode]) openMode = kIOHIDOptionsTypeSeizeDevice;
+    IOReturn ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
+
+    if (ioReturnValue == KERN_SUCCESS) {
+        queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
+        if (queue) {
+            result = (*queue)->create(queue, 0, 12);    //depth: maximum number of elements in queue before oldest elements in queue begin to be lost.
+
+            int i=0;
+            for(i=0; i<[allCookies count]; i++) {
+                IOHIDElementCookie cookie = (IOHIDElementCookie)[[allCookies objectAtIndex:i] intValue];
+                (*queue)->addElement(queue, cookie, 0);
+            }
+
+            // add callback for async events
+            CFRunLoopSourceRef eventSource;
+            ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
+            if (ioReturnValue == KERN_SUCCESS) {
+                ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, self, NULL);
+                if (ioReturnValue == KERN_SUCCESS) {
+                    CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
+                    //start data delivery to queue
+                    (*queue)->start(queue);
+                    return YES;
+                } else {
+                    NSLog(@"Error when setting event callout");
+                }
+            } else {
+                NSLog(@"Error when creating async event source");
+            }
+        } else {
+            NSLog(@"Error when opening device");
+        }
+    }
+    return NO;
+}
+
+@end
+
+@implementation AppleRemoteApplicationDelegate
+
+- (id) initWithApplicationDelegate: (id) delegate {
+    if (self = [super init]) {
+        applicationDelegate = [delegate retain];
+    }
+    return self;
+}
+
+- (void) dealloc {
+    [applicationDelegate release];
+    [super dealloc];
+}
+
+- (id) applicationDelegate {
+    return applicationDelegate;
+}
+
+- (void)applicationWillBecomeActive:(NSNotification *)aNotification {
+    if ([applicationDelegate respondsToSelector: @selector(applicationWillBecomeActive:)]) {
+        [applicationDelegate applicationWillBecomeActive: aNotification];
+    }
+}
+- (void)applicationDidBecomeActive:(NSNotification *)aNotification {
+    [[AppleRemote sharedRemote] setListeningToRemote: YES];
+
+    if ([applicationDelegate respondsToSelector: @selector(applicationDidBecomeActive:)]) {
+        [applicationDelegate applicationDidBecomeActive: aNotification];
+    }
+}
+- (void)applicationWillResignActive:(NSNotification *)aNotification {
+    [[AppleRemote sharedRemote] setListeningToRemote: NO];
+
+    if ([applicationDelegate respondsToSelector: @selector(applicationWillResignActive:)]) {
+        [applicationDelegate applicationWillResignActive: aNotification];
+    }
+}
+- (void)applicationDidResignActive:(NSNotification *)aNotification {
+    if ([applicationDelegate respondsToSelector: @selector(applicationDidResignActive:)]) {
+        [applicationDelegate applicationDidResignActive: aNotification];
+    }
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
+    NSMethodSignature* signature = [super methodSignatureForSelector: aSelector];
+    if (signature == nil && applicationDelegate != nil) {
+        signature = [applicationDelegate methodSignatureForSelector: aSelector];
+    }
+    return signature;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation {
+    SEL aSelector = [invocation selector];
+
+    if (applicationDelegate==nil || [applicationDelegate respondsToSelector:aSelector]==NO) {
+        [super forwardInvocation: invocation];
+        return;
+    }
+
+    [invocation invokeWithTarget:applicationDelegate];
+}
+@end
index 758c44a97608a247f1a9357645e6d4840b488956..b9a5c8bd7f98836b2064175007c1145b306839e2 100644 (file)
@@ -55,9 +55,7 @@
  *****************************************************************************/
 @interface VLCOneSplitView : NSSplitView
 {
-    BOOL fixedCursorDuringResize;
 }
-@property (assign) BOOL fixedCursorDuringResize;
 - (float)sliderPosition;
 - (void)setSliderPosition:(float)newPosition;
 @end
 @interface NSImageView (VLCAppAdditions)
 - (BOOL)mouseDownCanMoveWindow;
 @end
+
+/*****************************************************************************
+ * NSImage (VLCAppAdditions)
+ *
+ *  Make the image view move the window by mouse down by default
+ *****************************************************************************/
+
+@interface NSImage (VLCAppAdditions)
+- (CGImageRef)CGImage;
+@end
index 84909fb413dc8ec23f020bc739a5546f67e5b074..a453113d421bfb28b6f6ca38aeff9d2d1d3156cd 100644 (file)
 
 /* Split view that supports slider animation */
 @implementation VLCOneSplitView
-@synthesize fixedCursorDuringResize;
+- (CGFloat)dividerThickness
+{
+    return 1.;
+}
+- (void)drawDividerInRect:(NSRect)aRect
+{
+    [self lockFocus];
+    [[NSColor blackColor] set];
+    NSRectFill(aRect);
+    [self unlockFocus];
+}
 - (float)sliderPosition
 {
-    return [[[self subviews] objectAtIndex:0] frame].size.height;
+    NSSize size = [[[self subviews] objectAtIndex:0] frame].size;
+    return [self isVertical] ? size.width : size.height;
 }
 - (void)setSliderPosition:(float)newPosition
 {
     }
     return [super defaultAnimationForKey: key];
 }
-- (void)adjustSubviews
-{
-    if( !fixedCursorDuringResize )
-    {
-        [super adjustSubviews];
-        return;
-    }
-    NSRect frame0 = [[[self subviews] objectAtIndex:0] frame];
-    NSRect frame1 = [[[self subviews] objectAtIndex:1] frame];
-    frame1.size.height = [self bounds].size.height - frame0.size.height - [self dividerThickness];
-    if( frame1.size.height < 0. )
-    {
-        float delta = -frame1.size.height;
-        frame1.size.height = 0.;
-        frame0.size.height -= delta;
-        frame1.origin.y = frame0.size.height + [self dividerThickness];
-        [[[self subviews] objectAtIndex:1] setFrame: frame0];
-    }
-    [[[self subviews] objectAtIndex:1] setFrame: frame1];
-}
 @end
 
 /*****************************************************************************
@@ -327,3 +318,17 @@ static NSMutableArray *blackoutWindows = NULL;
 }
 @end
 
+/*****************************************************************************
+ * NSImage (VLCAppAdditions)
+ *
+ *  Make the image view move the window by mouse down by default
+ *****************************************************************************/
+
+@implementation NSImage (VLCAppAdditions)
+- (CGImageRef)CGImage
+{
+    return [[NSBitmapImageRep imageRepWithData:[NSBitmapImageRep TIFFRepresentationOfImageRepsInArray: [self representations]]] CGImage];
+}
+@end
+
+
index 3ee0f17633b5e31360606419213b903d97c3e0c0..b427d04257f4dc7caa358edb2efbe705b73d51ca 100644 (file)
 #import <VLCKit/VLCKit.h>
 #import "VLCAppAdditions.h"
 
-@interface VLCBrowsableVideoView : VLCVideoView {
+@class VLCMainWindowController;
+@class VLCMediaListLayer;
+
+@interface VLCBrowsableVideoView : NSView {
     BOOL            menuDisplayed;
     NSArray *       itemsTree;
     NSRange         displayedItems;
@@ -50,6 +53,9 @@
     NSViewAnimation * fullScreenAnim1;
     NSViewAnimation * fullScreenAnim2;
     NSView * tempFullScreenView;
+    IBOutlet VLCMainWindowController * mainWindowController;
+    VLCVideoLayer * videoLayer;
+    VLCMediaListLayer * mediaListLayer;
 }
 
 /* Binds an nsarray to that property. But don't forget the set the access keys. */
@@ -60,6 +66,9 @@
 @property (readonly, retain) id selectedObject;
 
 @property (readwrite) BOOL fullScreen;
+@property (readonly) BOOL hasVideo;
+
+@property (readonly) VLCVideoLayer * videoLayer;
 
 /* Set up a specific action to do, on items that don't have node.
  * action first argument is the browsableVideoView. You can get the selected object,
@@ -70,4 +79,6 @@
 - (void)toggleMenu;
 - (void)displayMenu;
 - (void)hideMenu;
+
+- (IBAction)backToMediaListView:(id)sender;
 @end
index bb3baab3abaced4be637cc650928614d5bd6bdf6..b504b8b52210ba71eb11f7eb70d803d75f7d1069 100644 (file)
@@ -28,6 +28,8 @@
 
 #import "VLCBrowsableVideoView.h"
 #import "VLCAppAdditions.h"
+#import "VLCMediaListLayer.h"
+#import "VLCMainWindowController.h"
 
 /* TODO: We may want to clean up the private functions a bit... */
 
@@ -60,6 +62,7 @@
 
 @end
 
+#pragma mark -
 /******************************************************************************
  * VLCBrowsableVideoView
  */
@@ -71,6 +74,7 @@
 @synthesize selectedObject;
 @synthesize target;
 @synthesize action;
+@synthesize videoLayer;
 
 - (NSArray *)itemsTree {
     return itemsTree;
     }
 }
 
+- (BOOL)hasVideo
+{
+    return videoLayer.hasVideo;
+}
+
+/* Binded to VideoLayer's hasVideo */
+- (void)setHasVideo:(BOOL)hasVideo
+{
+    if( hasVideo )
+    {
+        [CATransaction begin];
+        [videoLayer removeFromSuperlayer];
+        [self.layer addSublayer:videoLayer];
+        videoLayer.frame = [self layer].bounds;
+        [videoLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
+        [mediaListLayer removeFromSuperlayer];
+        [CATransaction commit];
+    }
+    else
+    {
+        [CATransaction begin];
+        [mediaListLayer removeFromSuperlayer];
+        [self.layer addSublayer:mediaListLayer];
+        mediaListLayer.frame = [self layer].bounds;
+        [mediaListLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
+        [videoLayer removeFromSuperlayer];
+        [CATransaction commit];
+
+    }
+    [[self layer] setNeedsDisplay];
+    [self setNeedsDisplay:YES];
+}
+
 /* Initializer */
 - (void)awakeFromNib
 {
     selectedPath = [[NSIndexPath alloc] init];
     tempFullScreenView = [[NSView alloc] init];
     fullScreen = NO;
-    /* Observe our bindings */
-    //[self displayMenu];
-    //[self changeSelectedIndex:0];
+
+    videoLayer = [[VLCVideoLayer layer] retain];
+    [videoLayer addObserver:self forKeyPath:@"hasVideo" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
+    
+    [videoLayer setDelegate:self];
+    NSAssert( mainWindowController, @"No mainWindowController" );
+    [mainWindowController.mediaPlayer setVideoLayer: videoLayer];
+    mediaListLayer = [[VLCMediaListLayer layerWithMediaArrayController:mainWindowController.mediaArrayController] retain];
+    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
+    [mainWindowController.mediaArrayController setSelectsInsertedObjects:YES];
+    [mainWindowController.mediaArrayController setAvoidsEmptySelection:YES];
+    [[self layer] addSublayer:mediaListLayer];
+    mediaListLayer.frame = [self layer].bounds;
+    [mediaListLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
+
+    [[self layer] setNeedsDisplay];
+
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    if([keyPath isEqualToString:@"hasVideo"])
+    {
+        [self setHasVideo:[object hasVideo]];
+        return;
+    }
+    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
 }
 
 - (void)dealloc
 {
+    /* Previously registered in */
+    [videoLayer removeObserver:self forKeyPath:@"hasVideo"];
+
+    [mediaListLayer release];
+    [videoLayer release];
     [tempFullScreenView release];
     [selectedPath release];
     [super dealloc];
 }
 
+#pragma mark -
+/* Drag and drop */
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+    NSPasteboard *pboard;
+    pboard = [sender draggingPasteboard];
+    if ( [[pboard types] containsObject:NSFilenamesPboardType] &&
+        ![mainWindowController.mediaArrayController.contentMediaList isReadOnly] )
+    {
+        self.layer.borderColor = CGColorCreateGenericGray(0.5, 0.5);
+        self.layer.cornerRadius = 10.f;
+        self.layer.borderWidth = 10.0;
+        return NSDragOperationCopy;
+    }
+    return NSDragOperationNone;
+}
+
+- (void)draggingEnded:(id < NSDraggingInfo >)sender
+{
+    [CATransaction begin];
+    [CATransaction setValue:[NSNumber numberWithFloat:0.1] forKey:kCATransactionAnimationDuration];
+    self.layer.borderWidth = 0.;
+    [CATransaction commit];
+    [CATransaction begin];
+    [mainWindowController.mediaArrayController setFilterPredicate:nil];
+    [mainWindowController.mediaArrayController setSelectionIndex:[mainWindowController.mediaArrayController.contentMediaList count] - 1];
+    [CATransaction commit];
+}
+
+- (void)draggingExited:(id < NSDraggingInfo >)sender
+{
+    self.layer.borderWidth = 0.;
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+    NSPasteboard *pboard;
+    NSDragOperation sourceDragMask;
+    sourceDragMask = [sender draggingSourceOperationMask];
+    pboard = [sender draggingPasteboard];
+    if ( [[pboard types] containsObject:NSFilenamesPboardType] )
+    {
+        NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
+        VLCMediaList * mediaList = mainWindowController.mediaArrayController.contentMediaList;
+        if( [mediaList isReadOnly] )
+            return NO;
+
+        [CATransaction begin];
+        for( NSString * filePath in files )
+            [mediaList addMedia:[VLCMedia mediaWithPath:filePath]];
+        [CATransaction commit];
+    }
+    return YES;
+}
+
+- (void)showDrag
+{
+
+}
+
+#pragma mark -
 /* Hiding/Displaying the menu */
 
 - (void)hideMenu
         [self displayMenu];
 }
 
+- (IBAction)backToMediaListView:(id)sender
+{
+    [mainWindowController.mediaPlayer stop];
+    [self setHasVideo: NO];
+}
+
+#pragma mark -
+/* drawRect */
+
 - (void)drawRect:(NSRect)rect
 {
-    if( [[[self layer] sublayers] count] )
+    if( [self hasVideo] )
     {
-        /* Don't draw the empty view if we have a video output on screen */
-        [super drawRect:rect];
+        [[NSColor blackColor] set];
+        NSRectFill(rect);
         return;
     }
-    NSColor * bottomGradient = [NSColor colorWithCalibratedWhite:0.10 alpha:1.0];
-    NSColor * topGradient    = [NSColor colorWithCalibratedWhite:0.45 alpha:1.0];
-       NSGradient * gradient = [[NSGradient alloc] initWithStartingColor:bottomGradient endingColor:topGradient];
-    [gradient drawInRect:self.bounds angle:90.0];
+    NSColor * topGradient = [NSColor colorWithCalibratedWhite:.0f alpha:1.0];
+    NSColor * bottomGradient   = [NSColor colorWithCalibratedWhite:0.35f alpha:1.0];
+       NSGradient * gradient = [[NSGradient alloc] initWithColorsAndLocations:bottomGradient, 0.f, topGradient, 0.65f, topGradient, 1.f, nil];
+    [gradient drawInRect:self.bounds angle:100.0];
 }
 
-
+#pragma mark -
 /* Event handling */
 
 - (BOOL)acceptsFirstResponder
 
 - (void)mouseDown:(NSEvent *)theEvent
 {
-    if([theEvent clickCount] != 2)
+    if([theEvent clickCount] == 1)
+    {
+        NSRect rect1 = [self bounds];
+        NSRect rect2 = [self bounds];
+        rect1.origin.x += [self bounds].size.width * 4./5.;
+        rect1.size.width /= 5.;
+        rect2.size.width /= 5.;
+        if(NSPointInRect([self convertPoint:[theEvent locationInWindow] fromView:nil], rect1))
+        {
+            [mainWindowController.mediaArrayController selectNext:self];
+        }
+        else if(NSPointInRect([self convertPoint:[theEvent locationInWindow] fromView:nil], rect2))
+        {
+            [mainWindowController.mediaArrayController selectPrevious:self];
+        }
         return;
-
-    self.fullScreen = !self.fullScreen;
+    }
+    if([theEvent clickCount] == 2)
+    {
+        [mainWindowController mediaListViewItemDoubleClicked:self];
+        return;
+    }
+    if([theEvent clickCount] == 3)
+    {
+        self.fullScreen = !self.fullScreen;
+    }
 }
 
 - (void)keyDown:(NSEvent *)theEvent
         else
             [self hideMenu];
     }
-    else if(!menuDisplayed)
+    else if(!menuDisplayed && [[theEvent charactersIgnoringModifiers] characterAtIndex:0] ==  NSRightArrowFunctionKey)
     {
         [self displayMenu];
     }
 
 @end
 
+#pragma mark -
 /******************************************************************************
  * VLCBrowsableVideoView (Private)
  */
+
 @implementation VLCBrowsableVideoView (Private)
 + (CAScrollLayer *)menuLayer
 {
 
 @end
 
+#pragma mark -
+
 @implementation VLCBrowsableVideoView (FullScreenTransition)
 
 - (void)enterFullScreen:(NSScreen *)screen
index 38a5e3854e146ead35b26540956403c77bd3ea41..53dea37067b77d59213aa66ca165e61f3bdd2e18 100644 (file)
@@ -52,6 +52,9 @@
     VLCURLToRepresentedFileNameTransformer *urlToRepresentedFileName;
     urlToRepresentedFileName = [[[VLCURLToRepresentedFileNameTransformer alloc] init] autorelease];
     [NSValueTransformer setValueTransformer:(id)urlToRepresentedFileName forName:@"URLToRepresentedFileNameTransformer"];
+    VLCSelectionIndexToDescriptionTransformer *indexToDescription;
+    indexToDescription = [[[VLCSelectionIndexToDescriptionTransformer alloc] init] autorelease];
+    [NSValueTransformer setValueTransformer:(id)indexToDescription forName:@"SelectionIndexToDescriptionTransformer"];
 
     /***********************************
      * categories: Main content
     NSArray * mediaDiscoverers = [NSArray arrayWithObjects:
         [[[VLCMediaDiscoverer alloc] initWithName:@"shoutcasttv"] autorelease],
         [[[VLCMediaDiscoverer alloc] initWithName:@"shoutcast"] autorelease],
-        [[[VLCMediaDiscoverer alloc] initWithName:@"sap"] autorelease], nil];
-
-    NSArray * playlists = [NSMutableArray arrayWithObjects:[VLCMedia mediaAsNodeWithName:@"Default Playlist"], nil];
-
-    NSDictionary * playlistsAsDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                                [@"Playlists" uppercaseString], @"descriptionInCategoriesList",
-                                @"Playlists", @"descriptionInVideoView",
-                                [NSNumber numberWithBool:NO], @"selectableInCategoriesList",
-                                playlists, @"childrenInCategoriesList",
-                                playlists, @"childrenInVideoView",
-                                nil];
+        [[[VLCMediaDiscoverer alloc] initWithName:@"sap"] autorelease],
+        [[[VLCMediaDiscoverer alloc] initWithName:@"freebox"] autorelease], nil];
 
     self.categories = [NSArray arrayWithObjects:
                     [NSMutableDictionary dictionaryWithObjectsAndKeys:
@@ -79,7 +73,7 @@
                         mediaDiscoverers, @"childrenInCategoriesList",
                         mediaDiscoverers, @"childrenInVideoView",
                         nil],
-                    playlistsAsDictionary,
+                    [VLCMedia mediaAsNodeWithName:@"Playlist"],
                     nil];
 
     /* Execution will continue in applicationDidFinishLaunching */
index 0dcd50e6a1bad9f3c8c751a3fb6344d0e3e19210..03aff6146429de95e97232cf8ed0d8481b95188d 100644 (file)
 - (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)aMask
 {
     [self printStackTrace:exception];
-    NSRunCriticalAlertPanel(@"Exception not handled!",
+    NSLog(@"*** Exception Handled! %@: %@", [exception name], [exception reason]);
+    int ret = NSRunCriticalAlertPanel(@"Exception not handled!",
                             [NSString stringWithFormat:@"%@: %@\n\nBack trace has been printed to Console.\n\nWe will now wait for debugger connection...\n",
                                 [exception name], [exception reason]],
-                            @"Wait Debugger", nil, nil);
-    NSLog(@"*** Exception Handled! %@: %@", [exception name], [exception reason]);
+                            @"Quit", @"Wait Debugger", nil);
+    if( ret == NSOKButton )
+    {
+        [NSApp terminate:self];
+    }
     return YES;
 }
 
index 61b5fa0593fe25a48d2edd7ca7e6409ab6bc1418..e96d7eb0aad4a1be37395cfce14cd85c49d0d2dd 100644 (file)
@@ -26,7 +26,7 @@
 #import <Cocoa/Cocoa.h>
 #import <VLCKit/VLCKit.h>
 #import "VLCAppAdditions.h"
-#import "VLCMainWindow.h"
+#import "VLCMainWindowController.h"
 
 
 @interface VLCFullScreenControllerWindow : NSPanel
@@ -49,7 +49,7 @@
     BOOL active;
     
     /* Owner */
-    IBOutlet VLCMainWindow   * mainWindow;
+    IBOutlet VLCMainWindowController   * mainWindowController;
 
     /* Draging the window using its content */
     NSPoint mouseClic;
index e03a703ce6064cbce520c4aa2768a5259aa6dc1e..cc940c1ed52325655250a0c383d15a8598ad8722 100644 (file)
     hideWindowTimer = nil;
 
     /* WindowView setup */
-    [[mainWindow.videoView window] setAcceptsMouseMovedEvents:YES];
-    [[mainWindow.videoView window] makeFirstResponder:mainWindow.videoView];
-    [mainWindow.videoView setPostsBoundsChangedNotifications: YES];
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoViewDidChangeBounds:) name:NSViewBoundsDidChangeNotification object:(id)mainWindow.videoView];
+    [[mainWindowController.videoView window] setAcceptsMouseMovedEvents:YES];
+    [[mainWindowController.videoView window] makeFirstResponder:mainWindowController.videoView];
+    [mainWindowController.videoView setPostsBoundsChangedNotifications: YES];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoViewDidChangeBounds:) name:NSViewBoundsDidChangeNotification object:(id)mainWindowController.videoView];
 
     /* Make sure we can know when the mouse is inside us */
     [[self contentView] addTrackingRect:[[self contentView] bounds] owner:self userData:nil assumeInside:NO];
     [mediaPositionSlider setBackgroundImage:[NSImage imageNamed:@"fs_time_slider.png"]];
     [mediaPositionSlider setNeedsDisplay:YES];
 
-    [mediaPositionSlider bind:@"enabled" toObject:mainWindow.mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
-    [mediaPositionSlider bind:@"enabled2" toObject:mainWindow.mediaPlayer withKeyPath:@"seekable" options: nil];
+    [mediaPositionSlider bind:@"enabled" toObject:mainWindowController.mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [mediaPositionSlider bind:@"enabled2" toObject:mainWindowController.mediaPlayer withKeyPath:@"seekable" options: nil];
 
-    [mediaPositionSlider bind:@"value" toObject:mainWindow.mediaPlayer withKeyPath:@"position" options:
+    [mediaPositionSlider bind:@"value" toObject:mainWindowController.mediaPlayer withKeyPath:@"position" options:
         [NSDictionary dictionaryWithObjectsAndKeys:@"Float10000FoldTransformer", NSValueTransformerNameBindingOption,
                                                   [NSNumber numberWithBool:NO], NSConditionallySetsEnabledBindingOption, nil ]];
 
 
-    [fillScreenButton bind:@"value" toObject:mainWindow.videoView withKeyPath:@"fillScreen" options: nil];
-    [fullScreenButton bind:@"value" toObject:mainWindow.videoView withKeyPath:@"fullScreen" options: nil];
+    [fillScreenButton bind:@"value" toObject:mainWindowController.videoView withKeyPath:@"videoLayer.fillScreen" options: nil];
+    [fullScreenButton bind:@"value" toObject:mainWindowController.videoView withKeyPath:@"fullScreen" options: nil];
 
-    [mediaReadingProgressText bind:@"value" toObject:mainWindow.mediaPlayer withKeyPath:@"time.stringValue" options: nil];
-    [mediaDescriptionText bind:@"value" toObject:mainWindow.mediaPlayer withKeyPath:@"description" options: nil];
+    [mediaReadingProgressText bind:@"value" toObject:mainWindowController.mediaPlayer withKeyPath:@"time.stringValue" options: nil];
+    [mediaDescriptionText bind:@"value" toObject:mainWindowController.mediaPlayer withKeyPath:@"description" options: nil];
 
-    /* mediaPlayer */
-    [mediaPlayerPlayPauseStopButton bind:@"enabled" toObject:mainWindow.mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
-    [mediaPlayerPlayPauseStopButton bind:@"state"   toObject:mainWindow.mediaPlayer withKeyPath:@"playing" options: nil];
-    [mediaPlayerPlayPauseStopButton bind:@"alternateImage" toObject:mainWindow.mediaPlayer withKeyPath:@"stateAsFullScreenButtonAlternateImage" options: nil];
-    [mediaPlayerPlayPauseStopButton bind:@"image"   toObject:mainWindow.mediaPlayer withKeyPath:@"stateAsFullScreenButtonImage" options: nil];
-    [mediaPlayerBackwardPrevButton  bind:@"enabled" toObject:mainWindow.mediaPlayer withKeyPath:@"playing" options: nil];
-    [mediaPlayerForwardNextButton   bind:@"enabled" toObject:mainWindow.mediaPlayer withKeyPath:@"playing" options: nil];
-    [mediaPlayerForwardNextButton   setTarget:mainWindow.mediaPlayer];
+    /* mainWindowController.mediaPlayer */
+    [mediaPlayerPlayPauseStopButton bind:@"enabled" toObject:mainWindowController.mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [mediaPlayerPlayPauseStopButton bind:@"state"   toObject:mainWindowController.mediaPlayer withKeyPath:@"playing" options: nil];
+    [mediaPlayerPlayPauseStopButton bind:@"alternateImage" toObject:mainWindowController.mediaPlayer withKeyPath:@"stateAsFullScreenButtonAlternateImage" options: nil];
+    [mediaPlayerPlayPauseStopButton bind:@"image"   toObject:mainWindowController.mediaPlayer withKeyPath:@"stateAsFullScreenButtonImage" options: nil];
+    [mediaPlayerBackwardPrevButton  bind:@"enabled" toObject:mainWindowController.mediaPlayer withKeyPath:@"playing" options: nil];
+    [mediaPlayerForwardNextButton   bind:@"enabled" toObject:mainWindowController.mediaPlayer withKeyPath:@"playing" options: nil];
+    [mediaPlayerForwardNextButton   setTarget:mainWindowController.mediaPlayer];
     [mediaPlayerForwardNextButton   setAction:@selector(fastForward)];
-    [mediaPlayerBackwardPrevButton  setTarget:mainWindow.mediaPlayer];
+    [mediaPlayerBackwardPrevButton  setTarget:mainWindowController.mediaPlayer];
     [mediaPlayerBackwardPrevButton  setAction:@selector(rewind)];
-    [mediaPlayerPlayPauseStopButton setTarget:mainWindow.mediaPlayer];
+    [mediaPlayerPlayPauseStopButton setTarget:mainWindowController.mediaPlayer];
     [mediaPlayerPlayPauseStopButton setAction:@selector(pause)];
 
-    [self bind:@"fullScreen" toObject:mainWindow.videoView withKeyPath:@"fullScreen" options: nil];
+    [self bind:@"fullScreen" toObject:mainWindowController.videoView withKeyPath:@"fullScreen" options: nil];
     
     active = NO;
 }
 
 - (void)updateTrackingRect
 {
-    VLCVideoView * videoView = mainWindow.videoView;
+    VLCBrowsableVideoView * videoView = mainWindowController.videoView;
 
     if( videoViewTrackingArea )
     {
index 62934016d83ac105acf55e514d2252df186ffb5e..95a4189b96e4866e4417447cc5954a131859eb59 100644 (file)
  *****************************************************************************/
 
 #import <Cocoa/Cocoa.h>
-#import "VLCController.h"
-#import "VLCMediaArrayController.h"
-#import "VLCAppAdditions.h"
-#import "VLCBrowsableVideoView.h"
 
 @interface VLCMainWindow : NSWindow {
-    /* IB elements */
-    IBOutlet id mediaListItemFetchedStatus;
-    IBOutlet id mediaListItemsCount;
-    IBOutlet id mediaListSearchField;
-
-    IBOutlet NSOutlineView * categoriesListView;
-    IBOutlet NSTableView * mediaListView;
-
-    IBOutlet VLCBrowsableVideoView * videoView;
-    IBOutlet id fillScreenButton;
-    IBOutlet id fullScreenButton;
-    IBOutlet NSSlider * mediaReadingProgressSlider;
-    IBOutlet NSTextField * mediaReadingProgressText;
-    IBOutlet NSTextField * mediaDescriptionText;
-
-    IBOutlet NSSlider * mediaSoundVolume;
-
-    IBOutlet NSButton * mediaPlayerForwardNextButton;
-    IBOutlet NSButton * mediaPlayerBackwardPrevButton;
-    IBOutlet NSButton * mediaPlayerPlayPauseStopButton;
-
-    IBOutlet id navigatorViewToggleButton;
-    IBOutlet VLCOneSplitView * mainSplitView;
-    IBOutlet NSView * navigatorView;
-    IBOutlet NSView * videoPlayerAndControlView;
-    IBOutlet NSView * controlView;
-
-    IBOutlet NSButton * addPlaylistButton;
-    IBOutlet NSButton * removePlaylistButton;
-
-    /* Toolbar */
-    IBOutlet NSView * toolbarMediaAudioVolume;
-    IBOutlet NSView * toolbarMediaDescription;
-    IBOutlet NSView * toolbarMediaControl;
-
-    IBOutlet VLCMediaPlayer * mediaPlayer;
-
-    IBOutlet VLCController * controller; /* This is a VLCController binded to the File's Owner of the nib */
-
-    /* Controllers */
-    NSTreeController * categoriesTreeController;
-    VLCMediaArrayController * mediaArrayController;
-    
-    /* Window state */
-    CGFloat navigatorHeight;
 }
 
-@property BOOL navigatorViewVisible;
-@property (readonly) VLCMediaPlayer * mediaPlayer;
-@property (readonly) VLCBrowsableVideoView * videoView;
 @end
index 7d61470c5d02641d9e245995cd684d91f90c9e28..48f4e6c9ed4ccc28df340eee460f5053ac8abb52 100644 (file)
  *****************************************************************************/
 
 #import "VLCMainWindow.h"
-#import "ImageAndTextCell.h"
-#import "VLCMediaArrayController.h"
-#import "VLCBrowsableVideoView.h"
-#import "VLCAppAdditions.h"
-#import "VLCFullScreenControllerWindow.h"
 
-@interface VLCMainWindow (NavigatorViewHidingShowing)
-@property float contentHeight; /* animatable, keep the mainSplitView cursor at the same place, enabling playlist(navigator) togling */
-@end
-
-/******************************************************************************
- * VLCMainWindow (CategoriesListDelegate)
- */
-@implementation VLCMainWindow (CategoriesListDelegate)
-- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
-{
-    return [[item representedObject] isKindOfClass:[NSDictionary class]];
-}
-- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
-{
-    return !([[item representedObject] isKindOfClass:[NSDictionary class]]);
-}
-- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
-{
-    [cell setRepresentedObject:[item representedObject]];
-}
-@end
-
-/******************************************************************************
- * VLCMainWindow (CategoriesListDataSource)
- */
-@implementation VLCMainWindow (CategoriesListDataSource)
-/* Drag and drop */
-- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
-{
-    int i;
-
-    if(![item respondsToSelector:@selector(representedObject)])
-        return NO;
-    
-    NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
-    if( !droppedItems )
-        droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
-    if( !droppedItems )
-        droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
-
-    NSAssert( droppedItems, @"Dropped an unsupported object type on the outline View" );
-
-    VLCMediaList * mediaList = [(VLCMedia *)[item representedObject] subitems];
-
-    for (i = 0; i < [droppedItems count]; i++)
-    {
-        NSString * filename = [droppedItems objectAtIndex:i];
-               VLCMedia *media = [VLCMedia mediaWithPath:filename];
-        [mediaList lock];
-               [mediaList insertMedia:media atIndex:index+1];
-        [mediaList unlock];
-    }
-    return YES;
-}
-
-- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
-{
-    NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
-    if( !droppedItems )
-        droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
-    if( !droppedItems )
-        droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
-
-    if(! droppedItems ||
-       ![item respondsToSelector:@selector(representedObject)] ||
-       ![[item representedObject] isKindOfClass:[VLCMedia class]] )
-    {
-        return NSDragOperationNone;
-    }
-
-    return NSDragOperationMove;
-}
-@end
 
 /******************************************************************************
  * VLCMainWindow
  */
 @implementation VLCMainWindow
 
-@synthesize mediaPlayer;
-@synthesize videoView;
-
-- (void)awakeFromNib;
-{
-    NSTableColumn * tableColumn;
-
-    /* Check ib outlets */
-    NSAssert( mainSplitView, @"No split view or wrong split view");
-    NSAssert( fullScreenButton, @"No fullscreen button");
-
-    /***********************************
-     * Init the media player
-     */
-    [mediaPlayer setVideoView:videoView];
-
-    /***********************************
-     * CategoriesList OutlineView content
-     */
-    /* categoriesTreeController */ 
-    categoriesTreeController = [[NSTreeController alloc] init];
-    [categoriesTreeController setContent:controller.categories];
-  
-    [categoriesTreeController setChildrenKeyPath:@"childrenInCategoriesList"];
-    //[categoriesTreeController bind:@"contentArray" toObject:controller withKeyPath:@"arrayOfMasters" options:nil];
-
-    /* Bind the "name" table column */
-    tableColumn = [categoriesListView tableColumnWithIdentifier:@"name"];
-       [tableColumn bind:@"value" toObject: categoriesTreeController withKeyPath:@"arrangedObjects.descriptionInCategoriesList" options:nil];
-    [tableColumn setEditable:YES];
-    /* FIXME: this doesn't work obviously. */
-       [tableColumn bind:@"editable" toObject: categoriesTreeController withKeyPath:@"arrangedObjects.editableInCategoriesList" options:nil];
-
-    /* Use an ImageAndTextCell in the "name" table column */
-    ImageAndTextCell * cell = [[ImageAndTextCell alloc] init];
-    [cell setFont:[[tableColumn dataCell] font]];
-    [cell setImageKeyPath:@"image"];
-
-    [tableColumn setDataCell:cell];
-
-    /* Other setup */
-    [categoriesListView setIndentationMarkerFollowsCell:YES];
-    [categoriesListView setAutoresizesOutlineColumn:NO];
-    [categoriesListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
-    [categoriesListView setDelegate:self];
-
-    [categoriesListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
-    [categoriesListView setDataSource: self];
-
-    /***********************************
-     * mediaListView setup
-     */
-
-    mediaArrayController = [[VLCMediaArrayController alloc] init];
-
-    /* 1- Drag and drop */
-    [mediaListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
-    [mediaListView setDataSource:mediaArrayController];
-
-    /* 2- Double click */
-    [mediaListView setTarget:self];
-    [mediaListView setDoubleAction:@selector(mediaListViewItemDoubleClicked:)];
-
-       /* 3- binding for "title" column */
-    tableColumn = [mediaListView tableColumnWithIdentifier:@"title"];
-       [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.metaDictionary.title" options:nil];
-
-       /* 4- binding for "state" column */
-    tableColumn = [mediaListView tableColumnWithIdentifier:@"state"];
-       [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.stateAsImage" options:nil];
-
-    /* 5- Search & Predicate */
-    NSMutableDictionary * bindingOptions = [NSMutableDictionary dictionary];
-    [bindingOptions setObject:@"metaDictionary.title contains[c] $value" forKey:NSPredicateFormatBindingOption];
-    [bindingOptions setObject:@"No Title" forKey:NSDisplayNameBindingOption];
-    [mediaListSearchField bind:@"predicate" toObject: mediaArrayController withKeyPath:@"filterPredicate" options:bindingOptions];
-    
-    /* 6- Bind the @"contentArray" and contentMediaList of the mediaArrayController */
-    [mediaArrayController bind:@"contentArray" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.media" options:nil];
-    [mediaArrayController bind:@"contentMediaList" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.parentMediaList" options:nil];
-    
-    /* 7- Aspect */
-    [mediaListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
-    [mediaListView setAllowsTypeSelect:YES];
-
-    /***********************************
-     * videoView setup
-     */
-    [videoView setItemsTree:controller.categories];
-    [videoView setNodeKeyPath:@"childrenInVideoView"];
-    [videoView setContentKeyPath:@"descriptionInVideoView"];
-    [videoView setTarget:self];
-    [videoView setAction:@selector(videoViewItemClicked:)];
-    
-    /***********************************
-     * Toolbar setup
-     */
-    [[self toolbar] setDelegate:self];
-
-    /***********************************
-     * Other interface element setup
-     */
-
-    [mediaListItemsCount bind:@"displayPatternValue1" toObject:mediaArrayController withKeyPath:@"arrangedObjects.@count" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
-    [mediaListItemFetchedStatus bind:@"animate" toObject:categoriesTreeController withKeyPath:@"selection.currentlyFetchingItems" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
-
-    [fillScreenButton bind:@"value" toObject:videoView withKeyPath:@"fillScreen" options: nil];
-    [fullScreenButton bind:@"value" toObject:videoView withKeyPath:@"fullScreen" options: nil];
-    [fullScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
-    [fillScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
-
-    [mediaReadingProgressSlider bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
-    [mediaReadingProgressSlider bind:@"enabled2" toObject:mediaPlayer withKeyPath:@"seekable" options: nil];
-
-    [mediaReadingProgressSlider bind:@"value" toObject:mediaPlayer withKeyPath:@"position" options:
-        [NSDictionary dictionaryWithObjectsAndKeys:@"Float10000FoldTransformer", NSValueTransformerNameBindingOption,
-                                                  [NSNumber numberWithBool:NO], NSConditionallySetsEnabledBindingOption, nil ]];
-    [mediaReadingProgressText bind:@"value" toObject:mediaPlayer withKeyPath:@"time.stringValue" options: nil];
-    [mediaDescriptionText bind:@"value" toObject:mediaPlayer withKeyPath:@"description" options: nil];
-    [self bind:@"representedFilename" toObject:mediaPlayer withKeyPath:@"media.url" options: [NSDictionary dictionaryWithObject:@"URLToRepresentedFileNameTransformer" forKey:NSValueTransformerNameBindingOption]];
-    [self bind:@"title" toObject:mediaPlayer withKeyPath:@"description" options: nil];
-
-    [navigatorViewToggleButton bind:@"value" toObject:self withKeyPath:@"navigatorViewVisible" options: nil];
-
-    /* Playlist buttons */
-    [removePlaylistButton bind:@"enabled" toObject:categoriesTreeController withKeyPath:@"selection.editableInCategoriesList" options: nil];
-    [removePlaylistButton setTarget:categoriesTreeController];
-    [removePlaylistButton setAction:@selector(remove:)];
-    [addPlaylistButton setTarget:controller];
-    [addPlaylistButton setAction:@selector(addPlaylist:)];
-
-    [mainSplitView setDelegate:self];
-
-    /* Sound */
-    [mediaSoundVolume bind:@"value" toObject:[VLCLibrary sharedLibrary] withKeyPath:@"audio.volume" options: nil];
-
-    /* mediaPlayer */
-    [mediaPlayerPlayPauseStopButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
-    [mediaPlayerPlayPauseStopButton bind:@"state"   toObject:mediaPlayer withKeyPath:@"playing" options: nil];
-    [mediaPlayerPlayPauseStopButton bind:@"alternateImage" toObject:mediaPlayer withKeyPath:@"stateAsButtonAlternateImage" options: nil];
-    [mediaPlayerPlayPauseStopButton bind:@"image"   toObject:mediaPlayer withKeyPath:@"stateAsButtonImage" options: nil];
-    [mediaPlayerBackwardPrevButton  bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
-    [mediaPlayerForwardNextButton   bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
-    [mediaPlayerForwardNextButton   setTarget:mediaPlayer];
-    [mediaPlayerForwardNextButton   setAction:@selector(fastForward)];
-    [mediaPlayerBackwardPrevButton  setTarget:mediaPlayer];
-    [mediaPlayerBackwardPrevButton  setAction:@selector(rewind)];
-    [mediaPlayerPlayPauseStopButton setTarget:mediaPlayer];
-    [mediaPlayerPlayPauseStopButton setAction:@selector(pause)];
-
-    /* Last minute setup */
-    [categoriesListView expandItem:nil expandChildren:YES];
-    [categoriesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[categoriesListView numberOfRows] > 0 ? [categoriesListView numberOfRows]-1 : 0] byExtendingSelection:NO];
-}
-
-- (void)dealloc
-{
-    [navigatorView release];
-    [mediaPlayer release];
-    [categoriesTreeController release];
-    [mediaArrayController release];
-    [super dealloc];
-}
-
-- (void)mediaListViewItemDoubleClicked:(id)sender
-{
-    if([[mediaArrayController selectedObjects] count] <= 0 )
-        return;
-    [mediaPlayer setMedia:[[mediaArrayController selectedObjects] objectAtIndex:0]];
-    [mediaPlayer play];
-}
-
-- (void)videoViewItemClicked:(id)sender
-{
-    id object = [sender selectedObject];
-    NSAssert( [object isKindOfClass:[VLCMedia class]], @"Object is not a VLCMedia" );
-
-    [mediaPlayer setMedia:object];
-    [mediaPlayer play];
-}
-
-- (BOOL)videoViewVisible
-{
-    NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
-    return  ([[[mainSplitView subviews] objectAtIndex:0] frame].size.height > 50.);
-}
-
-- (BOOL)navigatorViewVisible
-{
-    NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
-    return  ([[[mainSplitView subviews] objectAtIndex:1] frame].size.height > 6.);
-}
-
-
-- (void)setNavigatorViewVisible:(BOOL)visible
-{
-    NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
-    if(!([self navigatorViewVisible] ^ visible))
-        return; /* Nothing to do */
-    
-    if(visible)
-    {
-        /* Show the navigator view (playlist view) */
-        if( navigatorHeight < 100.f ) navigatorHeight = 100.f;
-        if( ![self videoViewVisible] && ![self navigatorViewVisible] )
-        {
-            /* Nothing is visible, only our toolbar */
-            NSRect frame = [self frame];
-            frame.origin.y += navigatorHeight;
-            frame.size.height += navigatorHeight;
-            [[self animator] setFrame:frame display:YES];
-        }
-        else
-            [[self animator] setContentHeight:[[self contentView] frame].size.height + navigatorHeight ];
-        /* Hack, because sliding cause some glitches */
-        [navigatorView moveSubviewsToVisible];
-    }
-    else
-    {
-        /* Hide the navigator view (playlist view) */
-        navigatorHeight = [navigatorView bounds].size.height;
-        [[self animator] setContentHeight:[[self contentView] frame].size.height - navigatorHeight];
-        /* Hack, because sliding cause some glitches */
-        [navigatorView moveSubviewsToVisible];
-    }
-}
 @end
 
-@implementation VLCMainWindow (SplitViewDelegating)
-- (CGFloat)splitView:(NSSplitView *)sender constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)offset
-{
-    CGFloat minHeight = 34.;
-
-    /* Hack, because sliding cause some glitches */
-    [navigatorView moveSubviewsToVisible];
-
-    /* Make a stuck point at the bottom of the nav view */
-    if( [sender bounds].size.height - proposedPosition < minHeight )
-         return [sender bounds].size.height;
-
-    return proposedPosition;
-}
-
-- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
-{
-    [sender adjustSubviews];
-
-    /* Hack, because sliding cause some glitches */
-    [navigatorView setFrame:[[navigatorView superview] bounds]];
-    [navigatorView moveSubviewsToVisible];
-}
-
-- (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
-{
-    /* Hack, because sliding cause some glitches */
-    [navigatorView moveSubviewsToVisible];
-
-    /* This could be changed from now on, so post a KVO notification */
-    [self willChangeValueForKey:@"navigatorViewVisible"];
-}
-- (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
-{
-    [self didChangeValueForKey:@"navigatorViewVisible"];
-}
-@end
-
-@implementation VLCMainWindow (NSWindowDelegating)
-- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
-{
-    if( proposedFrameSize.height < 120.f)
-        proposedFrameSize.height = [self minSize].height;
-    return proposedFrameSize;
-}
-@end
-
-@implementation VLCMainWindow (NavigatorViewHidingShowing)
-- (float)contentHeight
-{
-    return [self contentRectForFrameRect:[self frame]].size.height;
-}
-
-- (void)setContentHeight:(float)height
-{
-    /* Set the Height while keeping the mainSplitView at his current position */
-    [mainSplitView setFixedCursorDuringResize:YES];
-    NSRect contentRect = [self contentRectForFrameRect:[self frame]];
-    float delta = height - contentRect.size.height;
-    contentRect.size.height = height;
-       NSRect windowFrame = [self frameRectForContentRect:contentRect];
-    windowFrame.origin.y -= delta;
-    windowFrame = [self constrainFrameRect:windowFrame toScreen:[self screen]];
-    [self setFrame:windowFrame display:YES];
-    [mainSplitView setFixedCursorDuringResize:NO];
-}
-
-+ (id)defaultAnimationForKey:(NSString *)key
-{
-    if([key isEqualToString:@"contentHeight"])
-    {
-        return [CABasicAnimation animation];
-    }
-    return [super defaultAnimationForKey: key];
-}
-@end
-
-@implementation VLCMainWindow (NSToolbarDelegating)
-/* Our item identifiers */
-static NSString * VLCToolbarMediaControl     = @"VLCToolbarMediaControl";
-static NSString * VLCToolbarMediaAudioVolume = @"VLCToolbarMediaAudioVolume";
-static NSString * VLCToolbarMediaDescription = @"VLCToolbarMediaDescription";
-
-- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
-{
-    return [NSArray arrayWithObjects:
-                        NSToolbarCustomizeToolbarItemIdentifier,
-                        NSToolbarFlexibleSpaceItemIdentifier,
-                        NSToolbarSpaceItemIdentifier,
-                        NSToolbarSeparatorItemIdentifier,
-                        VLCToolbarMediaControl,
-                        VLCToolbarMediaAudioVolume,
-                        VLCToolbarMediaDescription,
-                        nil ];
-}
-
-- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
-{
-    return [NSArray arrayWithObjects:
-                        VLCToolbarMediaControl,
-                        VLCToolbarMediaAudioVolume,
-                        VLCToolbarMediaDescription,
-                        nil ];
-}
-
-- (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
-{
-    NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
-    if( [itemIdentifier isEqual: VLCToolbarMediaControl] )
-    {
-        [toolbarItem setLabel:@"Media Controls"];
-        [toolbarItem setPaletteLabel:@"Media Controls"];
-     
-        [toolbarItem setView:toolbarMediaControl];
-        [toolbarItem setMinSize:[[toolbarItem view] frame].size];
-        [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
-
-        /* TODO: setup a menu */
-    }
-    else if( [itemIdentifier isEqual: VLCToolbarMediaAudioVolume] )
-    {
-        [toolbarItem setLabel:@"Audio Volume"];
-        [toolbarItem setPaletteLabel:@"Audio Volume"];
-     
-        [toolbarItem setView:toolbarMediaAudioVolume];
-        [toolbarItem setMinSize:[[toolbarItem view] frame].size];
-        [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
-
-        /* TODO: setup a menu */
-    }
-    else  if( [itemIdentifier isEqual: VLCToolbarMediaDescription] )
-    {
-        [toolbarItem setLabel:@"Media Description"];
-        [toolbarItem setPaletteLabel:@"Media Description"];
-     
-        [toolbarItem setView:toolbarMediaDescription];
-        [toolbarItem setMinSize:[[toolbarItem view] frame].size];
-        [toolbarItem setMaxSize:NSMakeSize(10000 /* Can be really big */, NSHeight([[toolbarItem view] frame]))];
-
-        /* TODO: setup a menu */
-    }
-    else
-    {
-        /* itemIdentifier referred to a toolbar item that is not
-         * provided or supported by us or Cocoa
-         * Returning nil will inform the toolbar
-         * that this kind of item is not supported */
-        toolbarItem = nil;
-    }
-    return toolbarItem;
-}
-@end
diff --git a/projects/macosx/vlc_app/Sources/VLCMainWindowController.h b/projects/macosx/vlc_app/Sources/VLCMainWindowController.h
new file mode 100644 (file)
index 0000000..60527bc
--- /dev/null
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * VLCMainWindowController.h: VLCMainWindowController implementation
+ *****************************************************************************
+ * Copyright (C) 2007 Pierre d'Herbemont
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id: VLCMainWindow.h 24209 2008-01-09 22:05:17Z pdherbemont $
+ *
+ * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import "VLCController.h"
+#import "VLCMediaArrayController.h"
+#import "VLCAppAdditions.h"
+#import "VLCBrowsableVideoView.h"
+#import "VLCMainWindow.h"
+
+
+@interface VLCMainWindowController : NSWindowController
+{
+    IBOutlet VLCOneSplitView * mainSplitView;
+
+    /* Media List */
+    IBOutlet NSTableView * mediaListView;
+
+    /* Categories List */
+    IBOutlet NSOutlineView * categoriesListView;
+
+    IBOutlet NSButton * addPlaylistButton;
+    IBOutlet NSButton * removePlaylistButton;
+
+    /* Toolbar control buttons */
+    IBOutlet NSButton * mediaPlayerForwardNextButton;
+    IBOutlet NSButton * mediaPlayerBackwardPrevButton;
+    IBOutlet NSButton * mediaPlayerPlayPauseStopButton;
+
+
+    /* Toolbar */
+    IBOutlet NSView * toolbarMediaAudioVolume;
+    IBOutlet NSView * toolbarMediaDescription;
+    IBOutlet NSView * toolbarMediaControl;
+
+    /* Video */
+    IBOutlet VLCBrowsableVideoView * videoView;
+
+    /* Controllers */
+    IBOutlet NSTreeController * categoriesTreeController;
+    IBOutlet VLCMediaArrayController * mediaArrayController;
+    IBOutlet VLCMediaPlayer * mediaPlayer;
+    IBOutlet VLCController * controller; /* This is a VLCController binded to the File's Owner of the nib */
+
+    /* States */
+    float navigatorViewWidth;
+}
+
+@property BOOL navigatorViewVisible;
+
+- (void)setNavigatorViewVisible:(BOOL)wantsVisible animate:(BOOL)animate;
+
+@property (readonly) VLCMediaPlayer * mediaPlayer;
+@property (readonly) VLCBrowsableVideoView * videoView;
+@property (readonly) VLCMediaArrayController * mediaArrayController;
+
+- (IBAction)mediaListViewItemDoubleClicked:(id)sender;
+- (void)videoViewItemClicked:(id)sender;
+@end
diff --git a/projects/macosx/vlc_app/Sources/VLCMainWindowController.m b/projects/macosx/vlc_app/Sources/VLCMainWindowController.m
new file mode 100644 (file)
index 0000000..a0b422f
--- /dev/null
@@ -0,0 +1,405 @@
+/*****************************************************************************
+ * VLCMainWindowController.m: VLCMainWindowController implementation
+ *****************************************************************************
+ * Copyright (C) 2007 Pierre d'Herbemont
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id: VLCMainWindow.h 24209 2008-01-09 22:05:17Z pdherbemont $
+ *
+ * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "VLCMainWindowController.h"
+#import "VLCAppAdditions.h"
+#import "ImageAndTextCell.h"
+
+/******************************************************************************
+ * @implementation VLCMainWindowController
+ */
+
+@implementation VLCMainWindowController
+
+@synthesize mediaPlayer;
+@synthesize videoView;
+@synthesize mediaArrayController;
+
+- (void)awakeFromNib
+{
+    NSTableColumn * tableColumn;
+
+    /***********************************
+     * Init the media player
+     */
+
+    NSAssert( mediaPlayer, @"No mediaPlayer" );
+    
+    categoriesTreeController = [[NSTreeController alloc] init];
+    
+    /***********************************
+     * CategoriesList OutlineView content
+     */
+    /* categoriesTreeController */ 
+    NSAssert( categoriesTreeController, @"No categoriesTreeController" );
+    NSAssert( categoriesListView, @"No categoriesListView" );
+    NSAssert( controller, @"No controller" );
+
+    [categoriesTreeController setContent:controller.categories];
+    //[categoriesTreeController bind:@"content" toObject:controller withKeyPath:@"categories" options:nil];
+  
+    [categoriesTreeController setChildrenKeyPath:@"childrenInCategoriesList"];
+
+    /* Bind the "name" table column */
+    tableColumn = [categoriesListView tableColumnWithIdentifier:@"name"];
+    [tableColumn bind:@"value" toObject:categoriesTreeController withKeyPath:@"arrangedObjects.descriptionInCategoriesList" options:nil];
+
+    /* Use an ImageAndTextCell in the "name" table column */
+    ImageAndTextCell * cell = [[ImageAndTextCell alloc] init];
+    [cell setFont:[[tableColumn dataCell] font]];
+    [cell setImageKeyPath:@"image"];
+    [tableColumn setDataCell: cell];
+
+    /* Other setup */
+    [categoriesListView setIndentationMarkerFollowsCell:YES];
+    [categoriesListView setAutoresizesOutlineColumn:NO];
+    [categoriesListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
+    [categoriesListView setDelegate:self];
+
+    [categoriesListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
+    [categoriesListView setDataSource: self];
+
+    /***********************************
+     * mediaListView setup
+     */
+
+    /* 1- Drag and drop */
+    NSAssert( mediaArrayController, @"No mediaArrayController" );
+    NSAssert( mediaListView, @"No mediaListView" );
+    [mediaListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
+    [mediaListView setDataSource:mediaArrayController];
+       /* 3- binding for "title" column */
+    tableColumn = [mediaListView tableColumnWithIdentifier:@"title"];
+       [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.metaDictionary.title" options:nil];
+
+
+    /* 2- Double click */
+    [mediaListView setTarget:self];
+    [mediaListView setDoubleAction:@selector(mediaListViewItemDoubleClicked:)];
+
+
+       /* 4- binding for "state" column */
+    tableColumn = [mediaListView tableColumnWithIdentifier:@"state"];
+       [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.stateAsImage" options:nil];
+
+    /* 6- Bind the @"contentArray" and contentMediaList of the mediaArrayController */
+    [mediaArrayController bind:@"contentArray" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.media" options:nil];
+
+    [mediaArrayController bind:@"contentMediaList" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.parentMediaList" options:nil];
+    
+    /* 7- Aspect */
+    [mediaListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
+    [mediaListView setAllowsTypeSelect:YES];
+
+    /***********************************
+     * videoView setup
+     */
+    [videoView setItemsTree:controller.categories];
+    [videoView setNodeKeyPath:@"childrenInVideoView"];
+    [videoView setContentKeyPath:@"descriptionInVideoView"];
+    [videoView setTarget:self];
+    [videoView setAction:@selector(videoViewItemClicked:)];
+    
+    /***********************************
+     * Toolbar setup
+     */
+
+    /***********************************
+     * Other interface element setup
+     */
+#if 0
+
+    [mediaListItemsCount bind:@"displayPatternValue1" toObject:mediaArrayController withKeyPath:@"arrangedObjects.@count" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
+    [mediaListItemFetchedStatus bind:@"animate" toObject:categoriesTreeController withKeyPath:@"selection.currentlyFetchingItems" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
+
+    [fillScreenButton bind:@"value" toObject:videoView withKeyPath:@"fillScreen" options: nil];
+    [fullScreenButton bind:@"value" toObject:videoView withKeyPath:@"fullScreen" options: nil];
+    [fullScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [fillScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [mediaReadingProgressSlider bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [mediaReadingProgressSlider bind:@"enabled2" toObject:mediaPlayer withKeyPath:@"seekable" options: nil];
+
+    [mediaReadingProgressSlider bind:@"value" toObject:mediaPlayer withKeyPath:@"position" options:
+        [NSDictionary dictionaryWithObjectsAndKeys:@"Float10000FoldTransformer", NSValueTransformerNameBindingOption,
+                                                  [NSNumber numberWithBool:NO], NSConditionallySetsEnabledBindingOption, nil ]];
+    [mediaReadingProgressText bind:@"value" toObject:mediaPlayer withKeyPath:@"time.stringValue" options: nil];
+    [mediaDescriptionText bind:@"value" toObject:mediaPlayer withKeyPath:@"description" options: nil];
+    [self bind:@"representedFilename" toObject:mediaPlayer withKeyPath:@"media.url" options: [NSDictionary dictionaryWithObject:@"URLToRepresentedFileNameTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [self bind:@"title" toObject:mediaPlayer withKeyPath:@"description" options: nil];
+
+    [navigatorViewToggleButton bind:@"value" toObject:self withKeyPath:@"navigatorViewVisible" options: nil];
+#endif
+
+    /* Playlist buttons */
+#if 0
+    [removePlaylistButton bind:@"enabled" toObject:categoriesTreeController withKeyPath:@"selection.editableInCategoriesList" options: nil];
+#endif
+    [removePlaylistButton setTarget:categoriesTreeController];
+    [removePlaylistButton setAction:@selector(remove:)];
+    [addPlaylistButton setTarget:controller];
+    [addPlaylistButton setAction:@selector(addPlaylist:)];
+
+    /* mediaPlayer */
+#if 0
+    [mediaPlayerPlayPauseStopButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
+    [mediaPlayerPlayPauseStopButton bind:@"state"   toObject:mediaPlayer withKeyPath:@"playing" options: nil];
+    [mediaPlayerPlayPauseStopButton bind:@"alternateImage" toObject:mediaPlayer withKeyPath:@"stateAsButtonAlternateImage" options: nil];
+    [mediaPlayerPlayPauseStopButton bind:@"image"   toObject:mediaPlayer withKeyPath:@"stateAsButtonImage" options: nil];
+    [mediaPlayerBackwardPrevButton  bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
+    [mediaPlayerForwardNextButton   bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
+#endif
+
+    [mediaPlayerForwardNextButton   setTarget:mediaPlayer];
+    [mediaPlayerForwardNextButton   setAction:@selector(fastForward)];
+    [mediaPlayerBackwardPrevButton  setTarget:mediaPlayer];
+    [mediaPlayerBackwardPrevButton  setAction:@selector(rewind)];
+    [mediaPlayerPlayPauseStopButton setTarget:mediaPlayer];
+    [mediaPlayerPlayPauseStopButton setAction:@selector(pause)];
+
+    /* Last minute setup */
+    [categoriesListView expandItem:nil expandChildren:YES];
+    [categoriesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[categoriesListView numberOfRows] > 0 ? [categoriesListView numberOfRows]-1 : 0] byExtendingSelection:NO];
+    [self setNavigatorViewVisible:NO animate:NO];
+    [self showWindow:self];
+    [mainSplitView setDelegate:self];
+}
+
+- (BOOL)navigatorViewVisible
+{
+    return [mainSplitView sliderPosition] <= [mainSplitView bounds].size.width - [mainSplitView dividerThickness] - 30.f /* To be tolerant */;
+}
+
+- (void)setNavigatorViewVisible:(BOOL)wantsVisible animate:(BOOL)animate
+{
+    if( [self navigatorViewVisible] == wantsVisible )
+        return;
+
+    if( !animate ) [self willChangeValueForKey:@"navigatorViewVisible"];
+
+    VLCOneSplitView * splitView = animate ? [mainSplitView animator] : mainSplitView;
+        
+    if( wantsVisible )
+    {
+        if( navigatorViewWidth >= [mainSplitView bounds].size.width - 200.f )
+            navigatorViewWidth = [mainSplitView bounds].size.width - 200.f;
+        [splitView setSliderPosition:navigatorViewWidth];
+    }
+    else
+    {
+        navigatorViewWidth = [videoView frame].size.width;
+        [splitView setSliderPosition:[mainSplitView bounds].size.width - [mainSplitView dividerThickness]];
+    }
+    if( !animate ) [self didChangeValueForKey:@"navigatorViewVisible"];
+}
+
+- (void)setNavigatorViewVisible:(BOOL)wantsVisible
+{
+    [self setNavigatorViewVisible:wantsVisible animate:YES];
+}
+
+- (IBAction)mediaListViewItemDoubleClicked:(id)sender
+{
+    if([[mediaArrayController selectedObjects] count] <= 0 )
+        return;
+    [mediaPlayer setMedia:[[mediaArrayController selectedObjects] objectAtIndex:0]];
+    [mediaPlayer play];
+}
+
+- (void)videoViewItemClicked:(id)sender
+{
+    id object = [sender selectedObject];
+    NSAssert( [object isKindOfClass:[VLCMedia class]], @"Object is not a VLCMedia" );
+
+    [mediaPlayer setMedia:object];
+    [mediaPlayer play];
+}
+
+@end
+
+/******************************************************************************
+ * @implementation VLCMainWindowController (NSToolbarDelegating)
+ */
+
+@implementation VLCMainWindowController (NSToolbarDelegating)
+/* Our item identifiers */
+static NSString * VLCToolbarMediaControl     = @"VLCToolbarMediaControl";
+static NSString * VLCToolbarMediaAudioVolume = @"VLCToolbarMediaAudioVolume";
+static NSString * VLCToolbarMediaDescription = @"VLCToolbarMediaDescription";
+
+- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
+{
+    return [NSArray arrayWithObjects:
+                        NSToolbarCustomizeToolbarItemIdentifier,
+                        NSToolbarFlexibleSpaceItemIdentifier,
+                        NSToolbarSpaceItemIdentifier,
+                        NSToolbarSeparatorItemIdentifier,
+                        VLCToolbarMediaControl,
+                        VLCToolbarMediaAudioVolume,
+                        VLCToolbarMediaDescription,
+                        nil ];
+}
+
+- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
+{
+    return [NSArray arrayWithObjects:
+                        VLCToolbarMediaControl,
+                        VLCToolbarMediaAudioVolume,
+                        VLCToolbarMediaDescription,
+                        nil ];
+}
+
+- (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
+{
+    NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
+    if( [itemIdentifier isEqual: VLCToolbarMediaControl] )
+    {
+        [toolbarItem setLabel:@"Media Controls"];
+        [toolbarItem setPaletteLabel:@"Media Controls"];
+     
+        [toolbarItem setView:toolbarMediaControl];
+        [toolbarItem setMinSize:[[toolbarItem view] frame].size];
+        [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
+
+        /* TODO: setup a menu */
+    }
+    else if( [itemIdentifier isEqual: VLCToolbarMediaAudioVolume] )
+    {
+        [toolbarItem setLabel:@"Audio Volume"];
+        [toolbarItem setPaletteLabel:@"Audio Volume"];
+     
+        [toolbarItem setView:toolbarMediaAudioVolume];
+        [toolbarItem setMinSize:[[toolbarItem view] frame].size];
+        [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
+
+        /* TODO: setup a menu */
+    }
+    else  if( [itemIdentifier isEqual: VLCToolbarMediaDescription] )
+    {
+        [toolbarItem setLabel:@"Media Description"];
+        [toolbarItem setPaletteLabel:@"Media Description"];
+     
+        [toolbarItem setView:toolbarMediaDescription];
+        [toolbarItem setMinSize:[[toolbarItem view] frame].size];
+        [toolbarItem setMaxSize:NSMakeSize(10000 /* Can be really big */, NSHeight([[toolbarItem view] frame]))];
+
+        /* TODO: setup a menu */
+    }
+    else
+    {
+        /* itemIdentifier referred to a toolbar item that is not
+         * provided or supported by us or Cocoa
+         * Returning nil will inform the toolbar
+         * that this kind of item is not supported */
+        toolbarItem = nil;
+    }
+    return toolbarItem;
+}
+@end
+
+/******************************************************************************
+ * VLCMainWindowController (CategoriesListDelegate)
+ */
+@implementation VLCMainWindowController (CategoriesListDelegate)
+- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
+{
+    return [[item representedObject] isKindOfClass:[NSDictionary class]];
+}
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
+{
+    return !([[item representedObject] isKindOfClass:[NSDictionary class]]);
+}
+- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+    [cell setRepresentedObject:[item representedObject]];
+}
+@end
+
+/******************************************************************************
+ * VLCMainWindowController (CategoriesListDataSource)
+ */
+@implementation VLCMainWindowController (CategoriesListDataSource)
+/* Drag and drop */
+- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
+{
+    int i;
+
+    if(![item respondsToSelector:@selector(representedObject)])
+        return NO;
+    
+    NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
+    if( !droppedItems )
+        droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
+    if( !droppedItems )
+        droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
+
+    NSAssert( droppedItems, @"Dropped an unsupported object type on the outline View" );
+
+    VLCMediaList * mediaList = [(VLCMedia *)[item representedObject] subitems];
+
+    for (i = 0; i < [droppedItems count]; i++)
+    {
+        NSString * filename = [droppedItems objectAtIndex:i];
+               VLCMedia *media = [VLCMedia mediaWithPath:filename];
+        [mediaList lock];
+               [mediaList insertMedia:media atIndex:index+1];
+        [mediaList unlock];
+    }
+    return YES;
+}
+
+- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
+{
+    NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
+    if( !droppedItems )
+        droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
+    if( !droppedItems )
+        droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
+
+    if(! droppedItems ||
+       ![item respondsToSelector:@selector(representedObject)] ||
+       ![[item representedObject] isKindOfClass:[VLCMedia class]] )
+    {
+        return NSDragOperationNone;
+    }
+
+    return NSDragOperationMove;
+}
+@end
+
+/******************************************************************************
+ * VLCMainWindowController (SplitViewDelegate)
+ */
+@implementation VLCMainWindowController (SplitViewDelegate)
+
+- (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
+{
+    [self willChangeValueForKey:@"navigatorViewVisible"];
+}
+- (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
+{
+    [self didChangeValueForKey:@"navigatorViewVisible"];
+}
+
+@end
index bcd99233fce2025c16219ffe97721de992092232..34f8a3e2e0e9fad1cf05c1ff0f8cbe40093ecb4b 100644 (file)
@@ -62,6 +62,9 @@
     NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
     if( !droppedItems )
         droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
+
+    NSAssert( contentMediaList, @"No contentMediaList" );
+
     for (i = 0; i < [droppedItems count]; i++)
     {
         NSString * filename = [droppedItems objectAtIndex:i];
diff --git a/projects/macosx/vlc_app/Sources/VLCMediaLayer.h b/projects/macosx/vlc_app/Sources/VLCMediaLayer.h
new file mode 100644 (file)
index 0000000..21812de
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  VLCMediaLayer.h
+//  VLC
+//
+//  Created by Pierre d'Herbemont on 1/14/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
+#import <VLCKit/VLCKit.h>
+
+
+@interface VLCMediaLayer : CALayer {
+    BOOL displayFullInformation;
+    VLCMedia * media;
+    CATextLayer * titleLayer;
+    CATextLayer * artistLayer;
+    CATextLayer * genreLayer;
+    CALayer * artworkLayer;
+}
+
++ (id)layerWithMedia:(VLCMedia *)media;
+
+@property (assign) BOOL displayFullInformation;
+@property (retain,readonly) VLCMedia * media;
+@property (retain,readonly) CATextLayer * titleLayer;
+@property (retain,readonly) CATextLayer * artistLayer;
+@property (retain,readonly) CATextLayer * genreLayer;
+@property (retain,readonly) CALayer * artworkLayer;
+
+@end
diff --git a/projects/macosx/vlc_app/Sources/VLCMediaLayer.m b/projects/macosx/vlc_app/Sources/VLCMediaLayer.m
new file mode 100644 (file)
index 0000000..ae99942
--- /dev/null
@@ -0,0 +1,217 @@
+//
+//  VLCMediaLayer.m
+//  VLC
+//
+//  Created by Pierre d'Herbemont on 1/14/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+#import "VLCMediaLayer.h"
+
+/*****************************************************************************
+ * Some configuration first. Defines the size of the artwork layer.
+ */
+static CGSize kArtworkSize = { 256., 256. };
+
+/*****************************************************************************
+ * @interface VLCMediaLayer (Private)
+ */
+
+@interface VLCMediaLayer (Private)
+- (void)updateSublayers;
+@end
+
+/*****************************************************************************
+ * @interface VLCMediaLayer ()
+ */
+@interface VLCMediaLayer ()
+@property (retain,readwrite) VLCMedia * media;
+@property (retain,readwrite) CATextLayer * titleLayer;
+@property (retain,readwrite) CATextLayer * artistLayer;
+@property (retain,readwrite) CATextLayer * genreLayer;
+@property (retain,readwrite) CALayer * artworkLayer;
+@end
+
+/*****************************************************************************
+ * @implementation VLCMediaLayer
+ */
+@implementation VLCMediaLayer
+@synthesize displayFullInformation;
+@synthesize media;
+@synthesize titleLayer;
+@synthesize genreLayer;
+@synthesize artistLayer;
+@synthesize artworkLayer;
+
++ (id)layer
+{
+    return [self layerWithMedia:[VLCMedia mediaAsNodeWithName:@"Empty Media"]];
+}
++ (id)layerWithMedia:(VLCMedia *)aMedia
+{
+    VLCMediaLayer * me = [super layer];
+
+    if(!me) return nil;
+
+    me.media = aMedia;
+    me.displayFullInformation = YES;
+
+    /* Set the default layout */
+    me.titleLayer = [CATextLayer layer];
+    me.artistLayer = [CATextLayer layer];
+    me.genreLayer = [CATextLayer layer];
+    CALayer * textLayer = [CALayer layer];
+    NSDictionary * textStyle = [NSDictionary dictionaryWithObjectsAndKeys:
+                                        [NSNumber numberWithInteger:12], @"cornerRadius",
+                                        [NSValue valueWithSize:NSMakeSize(5, 0)], @"margin",
+                                        @"Lucida-Bold", @"font",
+                                        CGColorCreateGenericGray(0.5, 1.),@"foregroundColor",
+                                        [NSNumber numberWithInteger:18], @"fontSize",
+                                        [NSNumber numberWithFloat: .8], @"shadowOpacity",
+                                        [NSNumber numberWithFloat: 1.], @"shadowRadius",
+                                        kCAAlignmentLeft, @"alignmentMode",
+                                        nil];
+    NSDictionary * textTitleStyle = [NSDictionary dictionaryWithObjectsAndKeys:
+                                        [NSNumber numberWithInteger:12], @"cornerRadius",
+                                        [NSValue valueWithSize:NSMakeSize(5, 0)], @"margin",
+                                        @"Lucida", @"font",
+                                        [NSNumber numberWithInteger:26], @"fontSize",
+                                        [NSNumber numberWithFloat: .7], @"shadowOpacity",
+                                        [NSNumber numberWithFloat: 3.], @"shadowRadius",
+                                        kCAAlignmentLeft, @"alignmentMode",
+                                        nil];
+    /* First off, text */
+       me.titleLayer.style = textTitleStyle;
+    me.titleLayer.string = @"Title";
+    me.titleLayer.name = @"title";
+       [me.titleLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMinX offset:0.]];
+       [me.titleLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX offset:0.]];
+       [me.titleLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"artist" attribute:kCAConstraintMaxY offset:10.]];
+       me.artistLayer.style = textStyle;
+    me.artistLayer.string = @"Artist";
+    me.artistLayer.name = @"artist";
+       [me.artistLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"title" attribute:kCAConstraintMinX]];
+       [me.artistLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"title" attribute:kCAConstraintMaxX]];
+       [me.artistLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]];
+       me.genreLayer.style = textStyle;
+    me.genreLayer.string = @"Genre";
+    me.genreLayer.name = @"genre";
+       [me.genreLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"title" attribute:kCAConstraintMinX]];
+       [me.genreLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"title" attribute:kCAConstraintMaxX]];
+       [me.genreLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"artist" attribute:kCAConstraintMinY offset:-10.]];
+
+
+    [textLayer addSublayer:me.titleLayer];
+    [textLayer addSublayer:me.artistLayer];
+    [textLayer addSublayer:me.genreLayer];
+    textLayer.contentsGravity = kCAGravityCenter;
+    textLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
+
+    /* Empty layer for picture */
+    me.artworkLayer = [CALayer layer];
+    me.artworkLayer.backgroundColor = CGColorCreateGenericGray(0.5, 0.4);
+    me.artworkLayer.borderColor = CGColorCreateGenericRGB(1., 1., 1., .8);
+    me.artworkLayer.borderWidth = 3.0;
+
+   // me.artworkLayer.frame = CGRectMake(0.,0., kArtworkSize.width, kArtworkSize.height);
+    textLayer.frame = CGRectMake(0.,0., kArtworkSize.width, kArtworkSize.height);
+
+    /* Position the text and the artwork layer */
+    CALayer * container = [CALayer layer];
+    me.artworkLayer.name = @"artworkLayer";
+    textLayer.name = @"textLayer";
+    container.name = @"artContainer";
+    container.layoutManager = [CAConstraintLayoutManager layoutManager];
+
+    [container addSublayer:me.artworkLayer];
+       [container addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMinX offset:60.]];
+       [container addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]];
+       [container addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintHeight relativeTo:@"superlayer" attribute:kCAConstraintHeight scale:.6 offset:0.]];
+       [container addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMidX]];
+
+       [me.artworkLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"superlayer" attribute:kCAConstraintMinX]];
+       [me.artworkLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"superlayer" attribute:kCAConstraintMinY]];
+       [me.artworkLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"superlayer" attribute:kCAConstraintMaxY]];
+       [me.artworkLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX]];
+        
+       [textLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX relativeTo:@"artContainer" attribute:kCAConstraintMaxX]];
+       [textLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX relativeTo:@"superlayer" attribute:kCAConstraintMaxX ]];
+       [textLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"artContainer" attribute:kCAConstraintMidY]];
+
+    me.artworkLayer.zPosition = -30.f;
+
+    me.artworkLayer.shadowOpacity = .3;
+    me.artworkLayer.shadowRadius = 10.;
+    static CATransform3D rot, projection;
+    static BOOL transformInited = NO;
+    if( !transformInited )
+    {
+        rot = CATransform3DMakeRotation(.1
+        , 0., 1., 0.);
+            projection = CATransform3DIdentity; 
+        projection.m34 = 1. / -80.;
+        transformInited = YES;
+    }
+    me.artworkLayer.transform = rot;
+    container.sublayerTransform = projection;
+
+    me.layoutManager = [CAConstraintLayoutManager layoutManager];
+    [me addSublayer:textLayer];
+    [me addSublayer:container];
+
+    [me updateSublayers];
+
+    /* The following will trigger -observeValueForKeyPath: ofObject: change: context: */
+    [me.media addObserver:me forKeyPath:@"metaDictionary.title" options:NSKeyValueObservingOptionNew context:nil];
+    [me.media addObserver:me forKeyPath:@"metaDictionary.genre" options:NSKeyValueObservingOptionNew context:nil];
+    [me.media addObserver:me forKeyPath:@"metaDictionary.artist" options:NSKeyValueObservingOptionNew context:nil];
+    [me.media addObserver:me forKeyPath:@"metaDictionary.artwork" options:NSKeyValueObservingOptionNew context:nil];
+
+    return me;
+}
+
+- (void)dealloc
+{
+    /* Previously registered in +layerWithMediaArrayController: +layerWithMedia:*/
+    [self.media removeObserver:self forKeyPath:@"metaDictionary.title"];
+    [self.media removeObserver:self forKeyPath:@"metaDictionary.genre"];
+    [self.media removeObserver:self forKeyPath:@"metaDictionary.artist"];
+    [self.media removeObserver:self forKeyPath:@"metaDictionary.artwork"];
+
+    [super dealloc];
+}
+@end
+
+/*****************************************************************************
+ * @implementation VLCMediaLayer (Private)
+ */
+@implementation VLCMediaLayer (Private)
+
+- (void)updateSublayers
+{
+    [CATransaction begin];
+    self.titleLayer.string = [self.media.metaDictionary objectForKey:@"title"];
+    NSString * artist = [self.media.metaDictionary objectForKey:@"artist"];
+    self.artistLayer.string = artist ? artist : @"No Artist";
+    NSString * genre = [self.media.metaDictionary objectForKey:@"genre"];
+    self.genreLayer.string = genre ? genre : @"No Genre";
+    if( [self.media.metaDictionary objectForKey:@"artwork"] )
+    {
+        self.artworkLayer.contents = (id)[[self.media.metaDictionary objectForKey:@"artwork"] CGImage];
+        self.artworkLayer.contentsGravity = kCAGravityResizeAspect;
+        self.artworkLayer.borderWidth = 0.;
+        self.artworkLayer.backgroundColor = nil;
+    }
+    [CATransaction commit];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    if( [keyPath hasPrefix:@"metaDictionary"] )
+    {
+        [self updateSublayers];
+        return;
+    }
+    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+@end
diff --git a/projects/macosx/vlc_app/Sources/VLCMediaListLayer.h b/projects/macosx/vlc_app/Sources/VLCMediaListLayer.h
new file mode 100644 (file)
index 0000000..a3d01d5
--- /dev/null
@@ -0,0 +1,34 @@
+//
+//  VLCMediaListLayer.h
+//  VLC
+//
+//  Created by Pierre d'Herbemont on 1/14/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
+#import <VLCKit/VLCKit.h>
+#import "VLCMediaLayer.h"
+#import "VLCMediaArrayController.h"
+
+
+@interface VLCMediaListLayer : CALayer {
+    NSArray * content;
+    NSUInteger selectedIndex;
+    
+    VLCMediaLayer * previousLayer;
+    VLCMediaLayer * selectedLayer;
+    VLCMediaLayer * nextLayer;
+
+    VLCMediaArrayController * controller;
+}
+
++ (id)layer;
++ (id)layerWithMediaArrayController:(VLCMediaArrayController *)aController;
+
+@property (retain,readwrite) NSArray * content;
+@property (readwrite) NSUInteger selectedIndex;
+@property (retain,readwrite) VLCMediaArrayController * controller;
+
+@end
diff --git a/projects/macosx/vlc_app/Sources/VLCMediaListLayer.m b/projects/macosx/vlc_app/Sources/VLCMediaListLayer.m
new file mode 100644 (file)
index 0000000..b5f0139
--- /dev/null
@@ -0,0 +1,345 @@
+//
+//  VLCMediaListLayer.m
+//  VLC
+//
+//  Created by Pierre d'Herbemont on 1/14/08.
+//  Copyright 2008 __MyCompanyName__. All rights reserved.
+//
+
+#import "VLCMediaListLayer.h"
+
+/*****************************************************************************
+ * @implementation VLCMediaListLayer
+ */
+
+@interface VLCMediaListLayer (Private)
+- (VLCMediaLayer *)selectedLayer;
+- (VLCMediaLayer *)previousLayer;
+- (VLCMediaLayer *)nextLayer;
+- (void)changeSelectedLayerToNextIndex;
+- (void)changeSelectedLayerToPreviousIndex;
+- (void)resetLayers;
+
+
+- (void)setSelectedLayer:(VLCMediaLayer *)layer;
+- (void)setPreviousLayer:(VLCMediaLayer *)layer;
+- (void)setNextLayer:(VLCMediaLayer *)layer;
+@end
+
+/*****************************************************************************
+ * @implementation VLCMediaListLayer
+ */
+
+@implementation VLCMediaListLayer
+@synthesize selectedIndex;
+@synthesize content;
+@synthesize controller;
+
++ (id)layer
+{
+    VLCMediaListLayer * me = [super layer];
+
+    me.layoutManager = [CAConstraintLayoutManager layoutManager];
+
+    [CATransaction commit];
+
+    me->selectedIndex = NSNotFound;
+    return me;
+}
+
++ (id)layerWithMediaArrayController:(VLCMediaArrayController *)aController
+{
+    VLCMediaListLayer * me = [VLCMediaListLayer layer];
+    me.controller = aController;
+    
+    /* The following will trigger -observeValueForKeyPath: ofObject: change: context: */
+    [me.controller addObserver:me forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
+    [me.controller addObserver:me forKeyPath:@"selectionIndex" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
+    [me.controller addObserver:me forKeyPath:@"contentMediaList" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
+
+    return me;
+}
+
+- (void)dealloc
+{
+    /* Previously registered in +layerWithMediaArrayController: */
+    [self.controller removeObserver:self forKeyPath:@"arrangedObjects"];
+    [self.controller removeObserver:self forKeyPath:@"contentMediaList"];
+    [self.controller removeObserver:self forKeyPath:@"selectionIndex"];
+    [super dealloc];
+}
+@end
+
+/*****************************************************************************
+ * @implementation VLCMediaListLayer (Private)
+ */
+
+@implementation VLCMediaListLayer (Private)
++ (NSSet *)keyPathsForValuesAffectingSelectedLayer
+{
+    return [NSSet setWithObjects:@"selectedLayer", @"content", nil];
+}
+
+- (VLCMediaLayer *)selectedLayer
+{
+    VLCMedia * media = (self.selectedIndex != NSNotFound) ? [self.content objectAtIndex:self.selectedIndex ] : nil;
+    if( !media )
+    {
+        CATextLayer * layer = [CATextLayer layer];
+        CALayer * container = [CALayer layer];
+        container.layoutManager = [CAConstraintLayoutManager layoutManager];
+        if([self.controller.contentMediaList isReadOnly])
+            layer.string = @"Empty";
+        else if ([self.content count])
+            layer.string = @"Empty search.";
+        else
+            layer.string = @"Drag and Drop a movie or a music here.";
+        layer.alignmentMode = kCAAlignmentCenter;
+        layer.wrapped = YES;
+        [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]];
+        [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]];
+        [layer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth relativeTo:@"superlayer" attribute:kCAConstraintWidth]];
+        [container addSublayer:layer];
+        return (VLCMediaLayer *)container;
+    }
+
+    if( [selectedLayer isKindOfClass:[VLCMediaLayer class]] && [media compare:[selectedLayer media]] == NSOrderedSame )
+        return [[selectedLayer retain] autorelease];
+
+    return [VLCMediaLayer layerWithMedia:[self.content objectAtIndex:self.selectedIndex]];
+}
+
+- (VLCMediaLayer *)previousLayer
+{
+    if( self.selectedIndex == NSNotFound )
+        return nil;
+    VLCMedia * media = self.selectedIndex > 0 ? [self.content objectAtIndex:self.selectedIndex - 1] : nil;
+    if( !media )
+        return nil;
+
+    if( [previousLayer isKindOfClass:[VLCMediaLayer class]] && [media compare:[previousLayer media]] == NSOrderedSame )
+        return [[previousLayer retain] autorelease];
+    
+    return [VLCMediaLayer layerWithMedia: media ];
+}
+
+- (VLCMediaLayer *)nextLayer
+{
+    if( self.selectedIndex == NSNotFound )
+        return nil;
+    VLCMedia * media = self.selectedIndex + 1 < [content count] ? [self.content objectAtIndex:self.selectedIndex + 1] : nil;
+    if( !media )
+        return nil;
+
+    if( [nextLayer isKindOfClass:[VLCMediaLayer class]] && [media compare:[nextLayer media]] == NSOrderedSame )
+        return [[nextLayer retain] autorelease];
+    
+    return [VLCMediaLayer layerWithMedia: media ];
+}
+
+- (void)changeSelectedLayerToNextIndex
+{
+    if(!nextLayer)
+    {
+        /* Can't do anything */
+        return;
+    }
+    selectedIndex++;
+
+    /* Remove offscreen layer. Without actions */
+    if( previousLayer )
+    {
+        [CATransaction begin];
+        [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
+            [previousLayer removeFromSuperlayer];
+        [CATransaction commit];
+    }
+
+    [CATransaction begin];
+        if ( [[NSApp currentEvent] modifierFlags] & NSShiftKeyMask )
+            [CATransaction setValue:[NSNumber numberWithFloat:1.5] forKey:kCATransactionAnimationDuration];
+
+        [self setPreviousLayer: selectedLayer];        
+        [self setSelectedLayer: nextLayer];
+        [self setNextLayer: [self nextLayer]];
+    [CATransaction commit];
+
+    /* Move the new nextLayer layer on screen. Without Actions */
+    [CATransaction begin];
+    [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
+        [self addSublayer: nextLayer];
+    [CATransaction commit];
+}
+
+- (void)changeSelectedLayerToPreviousIndex
+{
+    if(!previousLayer)
+    {
+        /* Can't do anything */
+        return;
+    }
+    selectedIndex--;
+
+    /* Remove offscreen layer. Without actions */
+    if( nextLayer )
+    {
+        [CATransaction begin];
+        [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
+            [nextLayer removeFromSuperlayer];
+        [CATransaction commit];
+    }
+
+    [CATransaction begin];
+        if ( [[NSApp currentEvent] modifierFlags] & NSShiftKeyMask )
+            [CATransaction setValue:[NSNumber numberWithFloat:1.5] forKey:kCATransactionAnimationDuration];
+
+        [self setNextLayer: selectedLayer];        
+        [self setSelectedLayer: previousLayer];
+        [self setPreviousLayer: [self previousLayer]];
+    [CATransaction commit];
+
+    /* Move the new previous layer on screen. Without Actions */
+    [CATransaction begin];
+    [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
+        [self addSublayer:previousLayer];
+    [CATransaction commit];
+}
+
+- (void)gotoIndex:(NSUInteger)index;
+{
+    if( selectedIndex == index )
+        return;
+
+    if( selectedIndex > index )
+    {
+        /* It is ok to scroll five layers */
+        if( selectedIndex - index < 5  )
+        {
+            [self changeSelectedLayerToPreviousIndex];
+            return;
+        }
+        [self changeSelectedLayerToPreviousIndex];
+        [self changeSelectedLayerToPreviousIndex];
+        selectedIndex = index;
+        [self resetLayers];
+    }
+    else
+    {
+        if( index - selectedIndex < 5  )
+        {
+            [self changeSelectedLayerToNextIndex];
+            return;
+        }
+        [self changeSelectedLayerToNextIndex];
+        [self changeSelectedLayerToNextIndex];
+        selectedIndex = index;
+        [self resetLayers];
+    }
+}
+
+- (void)resetLayers
+{
+    VLCMediaLayer * layer;
+    [CATransaction begin];
+    layer = [self previousLayer];
+    if( previousLayer != layer )
+    {
+        if( previousLayer ) [self replaceSublayer:previousLayer with:layer];
+        else [self addSublayer:layer];
+        [self setPreviousLayer:layer];
+    }
+    layer = [self selectedLayer];
+    if( selectedLayer != layer )
+    {
+        if( selectedLayer ) [self replaceSublayer:selectedLayer with:layer];
+        else [self addSublayer:layer];
+        [self setSelectedLayer:layer];
+    }
+    layer = [self nextLayer];
+    if( nextLayer != layer )
+    {
+        if( nextLayer ) [self replaceSublayer:nextLayer with:layer];
+        else [self addSublayer:layer];
+        [self setNextLayer:layer];
+    }
+    [CATransaction commit];
+}
+
+
+- (void)setSelectedLayer:(VLCMediaLayer *)layer
+{
+    [selectedLayer autorelease];
+    if( !layer )
+    {
+        selectedLayer = nil;
+        return;
+    }
+    selectedLayer = [layer retain];
+    selectedLayer.frame = [self bounds];
+    [selectedLayer setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable];
+}
+
+- (void)setPreviousLayer:(VLCMediaLayer *)layer
+{
+    [previousLayer autorelease];
+    if( !layer )
+    {
+        previousLayer = nil;
+        return;
+    }
+    previousLayer = [layer retain];
+    CGRect frame = [self bounds];
+    frame.origin.x -= frame.size.width;
+    previousLayer.frame = frame;
+    [previousLayer setAutoresizingMask:kCALayerMaxXMargin|kCALayerHeightSizable];
+}
+
+- (void)setNextLayer:(VLCMediaLayer *)layer
+{
+    [nextLayer autorelease];
+    if( !layer )
+    {
+        nextLayer = nil;
+        return;
+    }
+    nextLayer = [layer retain];
+    CGRect frame = [self bounds];
+    frame.origin.x += frame.size.width;
+    nextLayer.frame = frame;
+    [nextLayer setAutoresizingMask:kCALayerMinXMargin|kCALayerHeightSizable];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    if([keyPath isEqualToString:@"selectionIndex"])
+    {
+        if ( selectedIndex == NSNotFound || [object selectionIndex] == NSNotFound )
+        {
+            selectedIndex = [object selectionIndex];
+            if(selectedIndex == NSNotFound  && [content count])
+            {
+                selectedIndex = 0;
+            }
+            [self resetLayers];
+            return;
+        }
+
+        [self gotoIndex: [object selectionIndex]];
+        return;
+    }
+    if([keyPath isEqualToString:@"arrangedObjects"] || [keyPath isEqualToString:@"contentMediaList"])
+    {
+        selectedIndex = [object selectionIndex];
+        if(selectedIndex == NSNotFound  && [[object arrangedObjects] count])
+        {
+            selectedIndex = 0;
+        }
+        [content release];
+        content = [[object arrangedObjects] retain];
+        [self resetLayers];
+        return;
+    }
+    [self observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+@end
index 2d08b4c7359ddf3f7b654230e56d2dc947e1d9e2..d30bcd063aa49ad6edef223ff66ced05a96cf370 100644 (file)
@@ -36,3 +36,7 @@
 }
 @end
 
+@interface VLCSelectionIndexToDescriptionTransformer : NSValueTransformer {
+}
+@end
+
index 86adcd4e8f7e7dd266c4e694c454606bf76bf2f2..41133aa35e71433ed09f1ebe4dff1a0396187640 100644 (file)
 
 @end
 
+@implementation VLCSelectionIndexToDescriptionTransformer
+
++ (Class)transformedValueClass
+{
+    return [NSNumber class];
+}
+
++ (BOOL)allowsReverseTransformation
+{
+    return NO;
+}
+
+- (NSString *)transformedValue:(id)value
+{
+    if( ![value isKindOfClass:[NSNumber class]])
+        return @"";
+
+    return [value intValue] == NSNotFound ? @"" : [NSString stringWithFormat:@"%@ of ", value];
+}
+
+@end
+
index 0312ff818339989e3b2e8dc9b17f5e1223d00c6a..32d3ba921ddb4d2d242d995f51075e7bd89eba4b 100644 (file)
@@ -13,7 +13,8 @@
                        buildPhases = (
                        );
                        dependencies = (
-                               632F3CC60D318E3A003BBC56 /* PBXTargetDependency */,
+                               63E930B80D5F43FA006B31CD /* PBXTargetDependency */,
+                               63E930B60D5F43E9006B31CD /* PBXTargetDependency */,
                        );
                        name = "vlc-contrib-core-framework-app";
                        productName = "vlc-contrib-core-framework-app";
@@ -24,7 +25,7 @@
                        buildPhases = (
                        );
                        dependencies = (
-                               632F3C260D316FAD003BBC56 /* PBXTargetDependency */,
+                               63E930BA0D5F4404006B31CD /* PBXTargetDependency */,
                                632F3C280D316FB5003BBC56 /* PBXTargetDependency */,
                        );
                        name = "vlc-framework-app";
@@ -33,7 +34,6 @@
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
-               632F3C380D31711C003BBC56 /* VLCKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 632F3C230D316F37003BBC56 /* VLCKit.framework */; };
                632F3E260D326FF0003BBC56 /* pause.png in Resources */ = {isa = PBXBuildFile; fileRef = 632F3E250D326FF0003BBC56 /* pause.png */; };
                633BD4BC0D2A90470012A314 /* VLCValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 633BD4AA0D2A90470012A314 /* VLCValueTransformer.m */; };
                633BD4BD0D2A90470012A314 /* VLCMediaArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 633BD4AC0D2A90470012A314 /* VLCMediaArrayController.m */; };
@@ -46,6 +46,9 @@
                633BD4C40D2A90470012A314 /* ImageAndTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 633BD4BA0D2A90470012A314 /* ImageAndTextCell.m */; };
                633BD4DA0D2A90C80012A314 /* dialog-error.png in Resources */ = {isa = PBXBuildFile; fileRef = 633BD4D80D2A90C80012A314 /* dialog-error.png */; };
                633BD4DB0D2A90C80012A314 /* applications-internet.png in Resources */ = {isa = PBXBuildFile; fileRef = 633BD4D90D2A90C80012A314 /* applications-internet.png */; };
+               63541FA90D3BA1DB001F6433 /* VLCMediaLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 63541FA80D3BA1DB001F6433 /* VLCMediaLayer.m */; };
+               635422050D3BE019001F6433 /* VLCMainWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 635422040D3BE019001F6433 /* VLCMainWindowController.m */; };
+               635424930D3C1394001F6433 /* VLCMediaListLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 635424920D3C1394001F6433 /* VLCMediaListLayer.m */; };
                63874B190D25960600F738AD /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63874B170D25960600F738AD /* MainWindow.xib */; };
                638B823B0D35294500128F2B /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 638B823A0D35294500128F2B /* QuickTime.framework */; };
                638F47110D216C8F008E4912 /* type_playlist.png in Resources */ = {isa = PBXBuildFile; fileRef = 638F47100D216C8F008E4912 /* type_playlist.png */; };
@@ -88,6 +91,7 @@
                63E768D80D3507EF00258089 /* fs_volume_slider_bar.png in Resources */ = {isa = PBXBuildFile; fileRef = 63E768BD0D3507EF00258089 /* fs_volume_slider_bar.png */; };
                63E768D90D3507EF00258089 /* fs_volume_slider_knob_highlight.png in Resources */ = {isa = PBXBuildFile; fileRef = 63E768BE0D3507EF00258089 /* fs_volume_slider_knob_highlight.png */; };
                63E76A530D35225700258089 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63E76A520D35225700258089 /* Carbon.framework */; };
+               63E930C60D5F4463006B31CD /* VLCKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63E930B20D5F430E006B31CD /* VLCKit.framework */; };
                8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
                8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
                8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
-               632F3C220D316F37003BBC56 /* PBXContainerItemProxy */ = {
+               632F3C270D316FB5003BBC56 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
-                       containerPortal = 633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */;
-                       proxyType = 2;
-                       remoteGlobalIDString = 8DC2EF5B0486A6940098B216;
-                       remoteInfo = VLCKit;
+                       containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 8D1107260486CEB800E47090;
+                       remoteInfo = VLC;
                };
-               632F3C250D316FAD003BBC56 /* PBXContainerItemProxy */ = {
+               63E930B10D5F430E006B31CD /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */;
-                       proxyType = 1;
-                       remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+                       proxyType = 2;
+                       remoteGlobalIDString = 8DC2EF5B0486A6940098B216;
                        remoteInfo = VLCKit;
                };
-               632F3C270D316FB5003BBC56 /* PBXContainerItemProxy */ = {
+               63E930B50D5F43E9006B31CD /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
                        proxyType = 1;
                        remoteGlobalIDString = 8D1107260486CEB800E47090;
                        remoteInfo = VLC;
                };
-               632F3CC50D318E3A003BBC56 /* PBXContainerItemProxy */ = {
+               63E930B70D5F43FA006B31CD /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */;
                        proxyType = 1;
                        remoteGlobalIDString = 633BD6D20D2ADE040012A314;
                        remoteInfo = "vlc-contrib-core-framework";
                };
+               63E930B90D5F4404006B31CD /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+                       remoteInfo = VLCKit;
+               };
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
                633BD4BB0D2A90470012A314 /* ImageAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageAndTextCell.h; path = Sources/ImageAndTextCell.h; sourceTree = "<group>"; };
                633BD4D80D2A90C80012A314 /* dialog-error.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "dialog-error.png"; path = "Icons/dialog-error.png"; sourceTree = "<group>"; };
                633BD4D90D2A90C80012A314 /* applications-internet.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "applications-internet.png"; path = "Icons/applications-internet.png"; sourceTree = "<group>"; };
-               633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = VLCKit.xcodeproj; path = ../Framework/VLCKit.xcodeproj; sourceTree = SOURCE_ROOT; };
+               633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = VLCKit.xcodeproj; path = ../framework/VLCKit.xcodeproj; sourceTree = SOURCE_ROOT; };
+               63541FA70D3BA1DB001F6433 /* VLCMediaLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCMediaLayer.h; path = Sources/VLCMediaLayer.h; sourceTree = "<group>"; };
+               63541FA80D3BA1DB001F6433 /* VLCMediaLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCMediaLayer.m; path = Sources/VLCMediaLayer.m; sourceTree = "<group>"; };
+               635422030D3BE019001F6433 /* VLCMainWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCMainWindowController.h; path = Sources/VLCMainWindowController.h; sourceTree = "<group>"; };
+               635422040D3BE019001F6433 /* VLCMainWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCMainWindowController.m; path = Sources/VLCMainWindowController.m; sourceTree = "<group>"; };
+               635424910D3C1394001F6433 /* VLCMediaListLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCMediaListLayer.h; path = Sources/VLCMediaListLayer.h; sourceTree = "<group>"; };
+               635424920D3C1394001F6433 /* VLCMediaListLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCMediaListLayer.m; path = Sources/VLCMediaListLayer.m; sourceTree = "<group>"; };
                63874B180D25960600F738AD /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainWindow.xib; sourceTree = "<group>"; };
                638B823A0D35294500128F2B /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickTime.framework; path = /System/Library/Frameworks/QuickTime.framework; sourceTree = "<absolute>"; };
                638F47100D216C8F008E4912 /* type_playlist.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = type_playlist.png; path = ../../../modules/gui/qt4/pixmaps/type_playlist.png; sourceTree = SOURCE_ROOT; };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               632F3C380D31711C003BBC56 /* VLCKit.framework in Frameworks */,
+                               63E930C60D5F4463006B31CD /* VLCKit.framework in Frameworks */,
                                8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
                                63E380DF0D1C6FD800FD6958 /* QuartzCore.framework in Frameworks */,
                                63A742B30D2759C1002D41A0 /* ExceptionHandling.framework in Frameworks */,
                        name = Frameworks;
                        sourceTree = "<group>";
                };
-               632F3C1B0D316F37003BBC56 /* Products */ = {
-                       isa = PBXGroup;
-                       children = (
-                               632F3C230D316F37003BBC56 /* VLCKit.framework */,
-                       );
-                       name = Products;
-                       sourceTree = "<group>";
-               };
                633BD4620D2A8DF30012A314 /* Internals */ = {
                        isa = PBXGroup;
                        children = (
                                633BD4B30D2A90470012A314 /* VLCBrowsableVideoView.m */,
                                63E768810D3503E200258089 /* VLCFullScreenControllerWindow.h */,
                                63E768820D3503E200258089 /* VLCFullScreenControllerWindow.m */,
+                               63541FA70D3BA1DB001F6433 /* VLCMediaLayer.h */,
+                               63541FA80D3BA1DB001F6433 /* VLCMediaLayer.m */,
+                               635424910D3C1394001F6433 /* VLCMediaListLayer.h */,
+                               635424920D3C1394001F6433 /* VLCMediaListLayer.m */,
                        );
                        name = Video;
                        sourceTree = "<group>";
                        children = (
                                633BD4B00D2A90470012A314 /* VLCMainWindow.h */,
                                633BD4AD0D2A90470012A314 /* VLCMainWindow.m */,
+                               635422030D3BE019001F6433 /* VLCMainWindowController.h */,
+                               635422040D3BE019001F6433 /* VLCMainWindowController.m */,
                        );
                        name = "Window Management";
                        sourceTree = "<group>";
                        name = FullScreenControllerWindow;
                        sourceTree = "<group>";
                };
+               63E930AA0D5F430E006B31CD /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               63E930B20D5F430E006B31CD /* VLCKit.framework */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
                        projectDirPath = "";
                        projectReferences = (
                                {
-                                       ProductGroup = 632F3C1B0D316F37003BBC56 /* Products */;
+                                       ProductGroup = 63E930AA0D5F430E006B31CD /* Products */;
                                        ProjectRef = 633BD69D0D2ACE520012A314 /* VLCKit.xcodeproj */;
                                },
                        );
 /* End PBXProject section */
 
 /* Begin PBXReferenceProxy section */
-               632F3C230D316F37003BBC56 /* VLCKit.framework */ = {
+               63E930B20D5F430E006B31CD /* VLCKit.framework */ = {
                        isa = PBXReferenceProxy;
                        fileType = wrapper.framework;
                        path = VLCKit.framework;
-                       remoteRef = 632F3C220D316F37003BBC56 /* PBXContainerItemProxy */;
+                       remoteRef = 63E930B10D5F430E006B31CD /* PBXContainerItemProxy */;
                        sourceTree = BUILT_PRODUCTS_DIR;
                };
 /* End PBXReferenceProxy section */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/sh;
-                       shellScript = "if test \"$SYMROOT\" == \"$PROJECT_DIR/build\"\nthen\n\tVLCFRAMEWORK=\"$SRCROOT/../Framework/build/$CONFIGURATION/VLCKit.framework\"\nelse\n\tVLCFRAMEWORK=\"$SYMROOT/$CONFIGURATION/VLCKit.framework\"\nfi\n/Developer/Library/PrivateFrameworks/DevToolsCore.framewo../../../extras/package/macosx/Resources/pbxcp -exclude .DS_Store -exclude CVS -exclude .svn -resolve-src-symlinks $VLCFRAMEWORK $BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH\n\n\n";
+                       shellScript = "if test \"$SYMROOT\" == \"$PROJECT_DIR/build\"\nthen\n\tVLCFRAMEWORK=\"$SRCROOT/../Framework/build/$CONFIGURATION/VLCKit.framework\"\nelse\n\tVLCFRAMEWORK=\"$SYMROOT/$CONFIGURATION/VLCKit.framework\"\nfi\n/Developer/Library/PrivateFrameworks/DevToolsCore.framework/Resources/pbxcp -exclude .DS_Store -exclude CVS -exclude .svn -resolve-src-symlinks $VLCFRAMEWORK $BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH\n\n\n";
                };
 /* End PBXShellScriptBuildPhase section */
 
                                633BD4C30D2A90470012A314 /* VLCAppAdditions.m in Sources */,
                                633BD4C40D2A90470012A314 /* ImageAndTextCell.m in Sources */,
                                63E768830D3503E200258089 /* VLCFullScreenControllerWindow.m in Sources */,
+                               63541FA90D3BA1DB001F6433 /* VLCMediaLayer.m in Sources */,
+                               635422050D3BE019001F6433 /* VLCMainWindowController.m in Sources */,
+                               635424930D3C1394001F6433 /* VLCMediaListLayer.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
-               632F3C260D316FAD003BBC56 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       name = VLCKit;
-                       targetProxy = 632F3C250D316FAD003BBC56 /* PBXContainerItemProxy */;
-               };
                632F3C280D316FB5003BBC56 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = 8D1107260486CEB800E47090 /* VLC */;
                        targetProxy = 632F3C270D316FB5003BBC56 /* PBXContainerItemProxy */;
                };
-               632F3CC60D318E3A003BBC56 /* PBXTargetDependency */ = {
+               63E930B60D5F43E9006B31CD /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 8D1107260486CEB800E47090 /* VLC */;
+                       targetProxy = 63E930B50D5F43E9006B31CD /* PBXContainerItemProxy */;
+               };
+               63E930B80D5F43FA006B31CD /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        name = "vlc-contrib-core-framework";
-                       targetProxy = 632F3CC50D318E3A003BBC56 /* PBXContainerItemProxy */;
+                       targetProxy = 63E930B70D5F43FA006B31CD /* PBXContainerItemProxy */;
+               };
+               63E930BA0D5F4404006B31CD /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = VLCKit;
+                       targetProxy = 63E930B90D5F4404006B31CD /* PBXContainerItemProxy */;
                };
 /* End PBXTargetDependency section */