1 /*****************************************************************************
2 * VLCMainWindow.m: VLCMainWindow implementation
3 *****************************************************************************
4 * Copyright (C) 2007 Pierre d'Herbemont
5 * Copyright (C) 2007 the VideoLAN team
8 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
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.
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.
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 *****************************************************************************/
25 #import "VLCMainWindow.h"
26 #import "ImageAndTextCell.h"
27 #import "VLCMediaArrayController.h"
28 #import "VLCBrowsableVideoView.h"
29 #import "VLCAppAdditions.h"
31 /******************************************************************************
32 * VLCMainWindow (MasterViewDataSource)
34 @implementation VLCMainWindow (MasterViewDelegate)
35 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
37 return [[item representedObject] isKindOfClass:[NSDictionary class]];
39 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
41 return !([[item representedObject] isKindOfClass:[NSDictionary class]]);
43 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
45 [cell setRepresentedObject:[item representedObject]];
49 @implementation VLCMainWindow (MasterViewDataSource)
51 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
55 if(![item respondsToSelector:@selector(representedObject)])
58 NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
60 droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
62 droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
64 NSAssert( droppedItems, @"Dropped an unsupported object type on the outline View" );
66 VLCMediaList * mediaList = [(VLCMedia *)[item representedObject] subitems];
68 for (i = 0; i < [droppedItems count]; i++)
70 NSString * filename = [droppedItems objectAtIndex:i];
71 VLCMedia *media = [VLCMedia mediaWithPath:filename];
73 [mediaList insertMedia:media atIndex:index+1];
79 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
81 NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
83 droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
85 droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
88 ![item respondsToSelector:@selector(representedObject)] ||
89 ![[item representedObject] isKindOfClass:[VLCMedia class]] )
91 return NSDragOperationNone;
94 return NSDragOperationMove;
98 /******************************************************************************
101 @implementation VLCMainWindow
102 - (void)awakeFromNib;
104 NSTableColumn * tableColumn;
106 /* Check ib outlets */
107 NSAssert( mainSplitView, @"No split view or wrong split view");
108 NSAssert( fullScreenButton, @"No fullscreen button");
110 /***********************************
111 * Init the media player
113 mediaPlayer = [[VLCMediaPlayer alloc] initWithVideoView:videoView];
115 /***********************************
116 * MasterView OutlineView content
119 treeController = [[NSTreeController alloc] init];
120 [treeController setContent:controller.arrayOfMasters];
122 [treeController setChildrenKeyPath:@"childrenInMasterView"];
123 //[treeController bind:@"contentArray" toObject:controller withKeyPath:@"arrayOfMasters" options:nil];
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];
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"];
137 [tableColumn setDataCell:cell];
140 [categoryList setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
141 [categoryList setDelegate:self];
143 [categoryList registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
144 [categoryList setDataSource: self];
146 /***********************************
150 mediaArrayController = [[VLCMediaArrayController alloc] init];
152 /* 1- Drag and drop */
153 [detailList registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
154 [detailList setDataSource:mediaArrayController];
156 /* 2- Double click */
157 [detailList setTarget:self];
158 [detailList setDoubleAction:@selector(detailListItemDoubleClicked:)];
160 /* 3- binding for "title" column */
161 tableColumn = [detailList tableColumnWithIdentifier:@"title"];
162 [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.metaDictionary.title" options:nil];
164 /* 4- binding for "state" column */
165 tableColumn = [detailList tableColumnWithIdentifier:@"state"];
166 [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.stateAsImage" options:nil];
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];
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];
179 [detailList setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
180 [detailList setAllowsTypeSelect:YES];
182 /***********************************
185 [videoView setItemsTree:controller.arrayOfVideoViewMasters];
186 [videoView setNodeKeyPath:@"childrenInVideoView"];
187 [videoView setContentKeyPath:@"descriptionInVideoView"];
188 [videoView setTarget:self];
189 [videoView setAction:@selector(videoViewItemClicked:)];
192 /***********************************
193 * Other interface element setup
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]];
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];
204 [mediaReadingProgressSlider bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
205 [mediaReadingProgressSlider bind:@"enabled2" toObject:mediaPlayer withKeyPath:@"seekable" options: nil];
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];
213 [navigatorViewToggleButton bind:@"value" toObject:self withKeyPath:@"navigatorViewVisible" options: nil];
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:)];
222 [mainSplitView setDelegate:self];
224 /* Last minute setup */
225 [categoryList expandItem:nil expandChildren:YES];
226 [categoryList selectRowIndexes:[NSIndexSet indexSetWithIndex:[categoryList numberOfRows] > 0 ? [categoryList numberOfRows]-1 : 0] byExtendingSelection:NO];
231 [navigatorView release];
232 [mediaPlayer release];
233 [treeController release];
234 [mediaArrayController release];
238 - (void)detailListItemDoubleClicked:(id)sender
240 [mediaPlayer setMedia:[[mediaArrayController selectedObjects] objectAtIndex:0]];
244 - (void)videoViewItemClicked:(id)sender
246 id object = [sender selectedObject];
247 NSAssert( [object isKindOfClass:[VLCMedia class]], @"Object is not a VLCMedia" );
249 [mediaPlayer setMedia:object];
253 - (BOOL)videoViewVisible
255 NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
256 return ([[[mainSplitView subviews] objectAtIndex:0] frame].size.height > 50.);
259 - (BOOL)navigatorViewVisible
261 NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
262 return ([[[mainSplitView subviews] objectAtIndex:1] frame].size.height > 6.);
266 - (void)setNavigatorViewVisible:(BOOL)visible
268 NSAssert( mainSplitView && [[mainSplitView subviews] count] == 2, @"No split view or wrong split view");
269 if(!([self navigatorViewVisible] ^ visible))
270 return; /* Nothing to do */
274 if( !navigatorHeight ) navigatorHeight = 100.f;
275 if( ![self videoViewVisible] && ![self navigatorViewVisible] )
277 NSRect frame = [self frame];
278 frame.origin.y -= navigatorHeight;
279 frame.size.height += navigatorHeight;
280 [[self animator] setFrame:frame display:YES];
283 [[mainSplitView animator] setSliderPosition:([mainSplitView bounds].size.height - navigatorHeight - [mainSplitView dividerThickness])];
284 /* Hack, because sliding cause some glitches */
285 [navigatorView moveSubviewsToVisible];
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];
302 @implementation VLCMainWindow (SplitViewDelegating)
303 - (CGFloat)splitView:(NSSplitView *)sender constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)offset
305 CGFloat minHeight = 34.;
307 /* Hack, because sliding cause some glitches */
308 [navigatorView moveSubviewsToVisible];
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;
319 - (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
321 [sender adjustSubviews];
323 /* Hack, because sliding cause some glitches */
324 [navigatorView setFrame:[[navigatorView superview] bounds]];
325 [navigatorView moveSubviewsToVisible];
328 - (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
330 /* Hack, because sliding cause some glitches */
331 [navigatorView moveSubviewsToVisible];
333 /* This could be changed from now on, so post a KVO notification */
334 [self willChangeValueForKey:@"navigatorViewVisible"];
336 - (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
338 [self didChangeValueForKey:@"navigatorViewVisible"];
342 @implementation VLCMainWindow (NSWindowDelegating)
343 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
345 if( proposedFrameSize.height < 120.f)
346 proposedFrameSize.height = [self minSize].height;
347 return proposedFrameSize;