MacOSX/Framework/VLC.xcodeproj: Fix the copy script again. SYMROOT is defined everywhere.
[vlc.git] / extras / MacOSX / VLC_app / Sources / VLCMainWindow.m
1 /*****************************************************************************
2  * VLCMainWindow.m: VLCMainWindow implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "VLCMainWindow.h"
26 #import "ImageAndTextCell.h"
27 #import "VLCMediaArrayController.h"
28 #import "VLCBrowsableVideoView.h"
29 #import "VLCAppAdditions.h"
30
31 /******************************************************************************
32  * VLCMainWindow (MasterViewDataSource)
33  */
34 @implementation VLCMainWindow (MasterViewDelegate)
35 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
36 {
37     return [[item representedObject] isKindOfClass:[NSDictionary class]];
38 }
39 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
40 {
41     return !([[item representedObject] isKindOfClass:[NSDictionary class]]);
42 }
43 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
44 {
45     [cell setRepresentedObject:[item representedObject]];
46 }
47 @end
48
49 @implementation VLCMainWindow (MasterViewDataSource)
50 /* Drag and drop */
51 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
52 {
53     int i;
54
55     if(![item respondsToSelector:@selector(representedObject)])
56         return NO;
57     
58     NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
59     if( !droppedItems )
60         droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
61     if( !droppedItems )
62         droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
63
64     NSAssert( droppedItems, @"Dropped an unsupported object type on the outline View" );
65
66     VLCMediaList * mediaList = [(VLCMedia *)[item representedObject] subitems];
67
68     for (i = 0; i < [droppedItems count]; i++)
69     {
70         NSString * filename = [droppedItems objectAtIndex:i];
71                 VLCMedia *media = [VLCMedia mediaWithPath:filename];
72         [mediaList lock];
73                 [mediaList insertMedia:media atIndex:index+1];
74         [mediaList unlock];
75     }
76     return YES;
77 }
78
79 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
80 {
81     NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
82     if( !droppedItems )
83         droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
84     if( !droppedItems )
85         droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
86
87     if(! droppedItems ||
88        ![item respondsToSelector:@selector(representedObject)] ||
89        ![[item representedObject] isKindOfClass:[VLCMedia class]] )
90     {
91         return NSDragOperationNone;
92     }
93
94     return NSDragOperationMove;
95 }
96 @end
97
98 /******************************************************************************
99  * VLCMainWindow
100  */
101 @implementation VLCMainWindow
102 - (void)awakeFromNib;
103 {
104     NSTableColumn * tableColumn;
105
106     /* Check ib outlets */
107     NSAssert( mainSplitView, @"No split view or wrong split view");
108     NSAssert( fullScreenButton, @"No fullscreen button");
109
110     /***********************************
111      * Init the media player
112      */
113     mediaPlayer = [[VLCMediaPlayer alloc] initWithVideoView:videoView];
114
115     /***********************************
116      * MasterView OutlineView content
117      */
118     /* treeController */ 
119     treeController = [[NSTreeController alloc] init];
120     [treeController setContent:controller.arrayOfMasters];
121   
122     [treeController setChildrenKeyPath:@"childrenInMasterView"];
123     //[treeController bind:@"contentArray" toObject:controller withKeyPath:@"arrayOfMasters" options:nil];
124
125     /* Bind the "name" table column */
126     tableColumn = [categoryList tableColumnWithIdentifier:@"name"];
127         [tableColumn bind:@"value" toObject: treeController withKeyPath:@"arrangedObjects.descriptionInMasterView" options:nil];
128     [tableColumn setEditable:YES];
129     /* FIXME: this doesn't work obviously. */
130         [tableColumn bind:@"editable" toObject: treeController withKeyPath:@"arrangedObjects.editableInMasterView" options:nil];
131
132     /* Use an ImageAndTextCell in the "name" table column */
133     ImageAndTextCell * cell = [[ImageAndTextCell alloc] init];
134     [cell setFont:[[tableColumn dataCell] font]];
135     [cell setImageKeyPath:@"image"];
136
137     [tableColumn setDataCell:cell];
138
139     /* Other setup */
140     [categoryList setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
141     [categoryList setDelegate:self];
142
143     [categoryList registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
144     [categoryList setDataSource: self];
145
146     /***********************************
147      * detailList setup
148      */
149
150     mediaArrayController = [[VLCMediaArrayController alloc] init];
151
152     /* 1- Drag and drop */
153     [detailList registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
154     [detailList setDataSource:mediaArrayController];
155
156     /* 2- Double click */
157     [detailList setTarget:self];
158     [detailList setDoubleAction:@selector(detailListItemDoubleClicked:)];
159
160         /* 3- binding for "title" column */
161     tableColumn = [detailList tableColumnWithIdentifier:@"title"];
162         [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.metaDictionary.title" options:nil];
163
164         /* 4- binding for "state" column */
165     tableColumn = [detailList tableColumnWithIdentifier:@"state"];
166         [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.stateAsImage" options:nil];
167
168     /* 5- Search & Predicate */
169     NSMutableDictionary * bindingOptions = [NSMutableDictionary dictionary];
170     [bindingOptions setObject:@"metaDictionary.title contains[c] $value" forKey:NSPredicateFormatBindingOption];
171     [bindingOptions setObject:@"No Title" forKey:NSDisplayNameBindingOption];
172     [detailSearchField bind:@"predicate" toObject: mediaArrayController withKeyPath:@"filterPredicate" options:bindingOptions];
173     
174     /* 6- Bind the @"contentArray" and contentMediaList of the mediaArrayController */
175     [mediaArrayController bind:@"contentArray" toObject:treeController withKeyPath:@"selection.childrenInMasterViewForDetailView.media" options:nil];
176     [mediaArrayController bind:@"contentMediaList" toObject:treeController withKeyPath:@"selection.childrenInMasterViewForDetailView.parentMediaList" options:nil];
177     
178     /* 7- Aspect */
179     [detailList setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
180     [detailList setAllowsTypeSelect:YES];
181
182     /***********************************
183      * videoView setup
184      */
185     [videoView setItemsTree:controller.arrayOfVideoViewMasters];
186     [videoView setNodeKeyPath:@"childrenInVideoView"];
187     [videoView setContentKeyPath:@"descriptionInVideoView"];
188     [videoView setTarget:self];
189     [videoView setAction:@selector(videoViewItemClicked:)];
190     
191
192     /***********************************
193      * Other interface element setup
194      */
195
196     [detailItemsCount bind:@"displayPatternValue1" toObject:mediaArrayController withKeyPath:@"arrangedObjects.@count" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
197     [detailItemFetchedStatus bind:@"animate" toObject:treeController withKeyPath:@"selection.currentlyFetchingItems" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
198
199     [fillScreenButton bind:@"value" toObject:videoView withKeyPath:@"fillScreen" options: [NSDictionary dictionaryWithObject:NSNegateBooleanTransformerName forKey:NSValueTransformerNameBindingOption]];
200     [fullScreenButton bind:@"value" toObject:videoView withKeyPath:@"fullScreen" options: nil];
201     [fullScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
202     [fillScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
203
204     [mediaReadingProgressSlider bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
205     [mediaReadingProgressSlider bind:@"enabled2" toObject:mediaPlayer withKeyPath:@"seekable" options: nil];
206
207     [mediaReadingProgressSlider bind:@"value" toObject:mediaPlayer withKeyPath:@"position" options:
208         [NSDictionary dictionaryWithObjectsAndKeys:@"Float10000FoldTransformer", NSValueTransformerNameBindingOption,
209                                                   [NSNumber numberWithBool:NO], NSConditionallySetsEnabledBindingOption, nil ]];
210     [mediaReadingProgressText bind:@"value" toObject:mediaPlayer withKeyPath:@"time.stringValue" options: nil];
211     [mediaDescriptionText bind:@"value" toObject:mediaPlayer withKeyPath:@"description" options: nil];
212
213     [navigatorViewToggleButton bind:@"value" toObject:self withKeyPath:@"navigatorViewVisible" options: nil];
214
215     /* Playlist buttons */
216     [removePlaylistButton bind:@"enabled" toObject:treeController withKeyPath:@"selection.editableInMasterView" options: nil];
217     [removePlaylistButton setTarget:treeController];
218     [removePlaylistButton setAction:@selector(remove:)];
219     [addPlaylistButton setTarget:controller];
220     [addPlaylistButton setAction:@selector(addPlaylist:)];
221
222     [mainSplitView setDelegate:self];
223
224     /* Last minute setup */
225     [categoryList expandItem:nil expandChildren:YES];
226     [categoryList selectRowIndexes:[NSIndexSet indexSetWithIndex:[categoryList numberOfRows] > 0 ? [categoryList numberOfRows]-1 : 0] byExtendingSelection:NO];
227 }
228
229 - (void)dealloc
230 {
231     [navigatorView release];
232     [mediaPlayer release];
233     [treeController release];
234     [mediaArrayController release];
235     [super dealloc];
236 }
237
238 - (void)detailListItemDoubleClicked:(id)sender
239 {
240     [mediaPlayer setMedia:[[mediaArrayController selectedObjects] objectAtIndex:0]];
241     [mediaPlayer play];
242 }
243
244 - (void)videoViewItemClicked:(id)sender
245 {
246     id object = [sender selectedObject];
247     NSAssert( [object isKindOfClass:[VLCMedia class]], @"Object is not a VLCMedia" );
248
249     [mediaPlayer setMedia:object];
250     [mediaPlayer play];
251 }
252
253 - (BOOL)videoViewVisible
254 {
255     NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
256     return  ([[[mainSplitView subviews] objectAtIndex:0] frame].size.height > 50.);
257 }
258
259 - (BOOL)navigatorViewVisible
260 {
261     NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
262     return  ([[[mainSplitView subviews] objectAtIndex:1] frame].size.height > 6.);
263 }
264
265
266 - (void)setNavigatorViewVisible:(BOOL)visible
267 {
268     NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
269     if(!([self navigatorViewVisible] ^ visible))
270         return; /* Nothing to do */
271     
272     if(visible)
273     {
274         if( !navigatorHeight ) navigatorHeight = 100.f;
275         if( ![self videoViewVisible] && ![self navigatorViewVisible] )
276         {
277             NSRect frame = [self frame];
278             frame.origin.y -= navigatorHeight;
279             frame.size.height += navigatorHeight;
280             [[self animator] setFrame:frame display:YES];
281         }
282         else
283             [[mainSplitView animator] setSliderPosition:([mainSplitView bounds].size.height - navigatorHeight - [mainSplitView dividerThickness])];
284         /* Hack, because sliding cause some glitches */
285         [navigatorView moveSubviewsToVisible];
286     }
287     else
288     {
289         navigatorHeight = [navigatorView bounds].size.height;
290         NSRect frame0 = [self frame];
291         NSRect frame1 = [[[mainSplitView subviews] objectAtIndex: 1] frame];
292         frame0.size.height -= frame1.size.height;
293         frame0.origin.y += frame1.size.height;
294         frame1.size.height = 0;
295         [[mainSplitView animator] setSliderPosition:([mainSplitView bounds].size.height)];
296         /* Hack, because sliding cause some glitches */
297         [navigatorView moveSubviewsToVisible];
298     }
299 }
300 @end
301
302 @implementation VLCMainWindow (SplitViewDelegating)
303 - (CGFloat)splitView:(NSSplitView *)sender constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)offset
304 {
305     CGFloat minHeight = 34.;
306
307     /* Hack, because sliding cause some glitches */
308     [navigatorView moveSubviewsToVisible];
309
310     /* Get the bottom of the navigator view to get stuck at some points */
311     if( [sender bounds].size.height - proposedPosition < minHeight*3./2. &&
312         [sender bounds].size.height - proposedPosition >= minHeight/2 )
313          return [sender bounds].size.height - minHeight;
314     if( [sender bounds].size.height - proposedPosition < minHeight/2 )
315          return [sender bounds].size.height;
316     return proposedPosition;
317 }
318
319 - (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
320 {
321     [sender  adjustSubviews];
322
323     /* Hack, because sliding cause some glitches */
324     [navigatorView setFrame:[[navigatorView superview] bounds]];
325     [navigatorView moveSubviewsToVisible];
326 }
327
328 - (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
329 {
330     /* Hack, because sliding cause some glitches */
331     [navigatorView moveSubviewsToVisible];
332     
333     /* This could be changed from now on, so post a KVO notification */
334     [self willChangeValueForKey:@"navigatorViewVisible"];
335 }
336 - (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
337 {
338     [self didChangeValueForKey:@"navigatorViewVisible"];
339 }
340 @end
341
342 @implementation VLCMainWindow (NSWindowDelegating)
343 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
344 {
345     if( proposedFrameSize.height < 120.f)
346         proposedFrameSize.height = [self minSize].height;
347     return proposedFrameSize;
348 }
349 @end