1 /*****************************************************************************
2 * VLCMainWindowController.m: VLCMainWindowController 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 "VLCMainWindowController.h"
26 #import "VLCAppAdditions.h"
27 #import "ImageAndTextCell.h"
29 /******************************************************************************
30 * @implementation VLCMainWindowController
33 @implementation VLCMainWindowController
35 @synthesize mediaPlayer;
36 @synthesize videoView;
37 @synthesize mediaArrayController;
38 @synthesize categoriesTreeController;
42 NSTableColumn * tableColumn;
44 /***********************************
45 * Init the media player
48 NSAssert( mediaPlayer, @"No mediaPlayer" );
49 [self willChangeValueForKey:@"categoriesTreeController"];
50 categoriesTreeController = [[NSTreeController alloc] init];
51 [self didChangeValueForKey:@"categoriesTreeController"];
53 /***********************************
54 * CategoriesList OutlineView content
56 /* categoriesTreeController */
57 NSAssert( categoriesTreeController, @"No categoriesTreeController" );
58 NSAssert( categoriesListView, @"No categoriesListView" );
59 NSAssert( controller, @"No controller" );
61 [categoriesTreeController setContent:controller.categories];
62 //[categoriesTreeController bind:@"content" toObject:controller withKeyPath:@"categories" options:nil];
64 [categoriesTreeController setChildrenKeyPath:@"childrenInCategoriesList"];
66 /* Bind the "name" table column */
67 tableColumn = [categoriesListView tableColumnWithIdentifier:@"name"];
68 [tableColumn bind:@"value" toObject:categoriesTreeController withKeyPath:@"arrangedObjects.descriptionInCategoriesList" options:nil];
70 /* Use an ImageAndTextCell in the "name" table column */
71 ImageAndTextCell * cell = [[ImageAndTextCell alloc] init];
72 [cell setFont:[[tableColumn dataCell] font]];
73 [cell setImageKeyPath:@"image"];
74 [tableColumn setDataCell: cell];
77 [categoriesListView setIndentationMarkerFollowsCell:YES];
78 [categoriesListView setAutoresizesOutlineColumn:NO];
79 [categoriesListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
80 [categoriesListView setDelegate:self];
82 [categoriesListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
83 [categoriesListView setDataSource: self];
85 /***********************************
89 /* 1- Drag and drop */
90 NSAssert( mediaArrayController, @"No mediaArrayController" );
91 NSAssert( mediaListView, @"No mediaListView" );
92 [mediaListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
93 [mediaListView setDataSource:mediaArrayController];
94 /* 3- binding for "title" column */
95 tableColumn = [mediaListView tableColumnWithIdentifier:@"title"];
96 [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.metaDictionary.title" options:nil];
100 [mediaListView setTarget:self];
101 [mediaListView setDoubleAction:@selector(mediaListViewItemDoubleClicked:)];
104 /* 4- binding for "state" column */
105 tableColumn = [mediaListView tableColumnWithIdentifier:@"state"];
106 [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.stateAsImage" options:nil];
108 /* 6- Bind the @"contentArray" and contentMediaList of the mediaArrayController */
109 [mediaArrayController bind:@"contentArray" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.media" options:nil];
111 [mediaArrayController bind:@"contentMediaList" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.parentMediaList" options:nil];
114 [mediaListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
115 [mediaListView setAllowsTypeSelect:YES];
117 /***********************************
120 [videoView setItemsTree:controller.categories];
121 [videoView setNodeKeyPath:@"childrenInVideoView"];
122 [videoView setContentKeyPath:@"descriptionInVideoView"];
123 [videoView setTarget:self];
124 [videoView setAction:@selector(videoViewItemClicked:)];
126 /***********************************
130 /***********************************
131 * Other interface element setup
135 [mediaListItemsCount bind:@"displayPatternValue1" toObject:mediaArrayController withKeyPath:@"arrangedObjects.@count" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
136 [mediaListItemFetchedStatus bind:@"animate" toObject:categoriesTreeController withKeyPath:@"selection.currentlyFetchingItems" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
138 [fillScreenButton bind:@"value" toObject:videoView withKeyPath:@"fillScreen" options: nil];
139 [fullScreenButton bind:@"value" toObject:videoView withKeyPath:@"fullScreen" options: nil];
140 [fullScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
141 [fillScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
142 [mediaReadingProgressSlider bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
143 [mediaReadingProgressSlider bind:@"enabled2" toObject:mediaPlayer withKeyPath:@"seekable" options: nil];
145 [mediaReadingProgressSlider bind:@"value" toObject:mediaPlayer withKeyPath:@"position" options:
146 [NSDictionary dictionaryWithObjectsAndKeys:@"Float10000FoldTransformer", NSValueTransformerNameBindingOption,
147 [NSNumber numberWithBool:NO], NSConditionallySetsEnabledBindingOption, nil ]];
148 [mediaReadingProgressText bind:@"value" toObject:mediaPlayer withKeyPath:@"time.stringValue" options: nil];
149 [mediaDescriptionText bind:@"value" toObject:mediaPlayer withKeyPath:@"description" options: nil];
150 [self bind:@"representedFilename" toObject:mediaPlayer withKeyPath:@"media.url" options: [NSDictionary dictionaryWithObject:@"URLToRepresentedFileNameTransformer" forKey:NSValueTransformerNameBindingOption]];
151 [self bind:@"title" toObject:mediaPlayer withKeyPath:@"description" options: nil];
153 [navigatorViewToggleButton bind:@"value" toObject:self withKeyPath:@"navigatorViewVisible" options: nil];
156 /* Playlist buttons */
158 [removePlaylistButton bind:@"enabled" toObject:categoriesTreeController withKeyPath:@"selection.editableInCategoriesList" options: nil];
160 [removePlaylistButton setTarget:categoriesTreeController];
161 [removePlaylistButton setAction:@selector(remove:)];
162 [addPlaylistButton setTarget:controller];
163 [addPlaylistButton setAction:@selector(addPlaylist:)];
167 [mediaPlayerPlayPauseStopButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
168 [mediaPlayerPlayPauseStopButton bind:@"state" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
169 [mediaPlayerPlayPauseStopButton bind:@"alternateImage" toObject:mediaPlayer withKeyPath:@"stateAsButtonAlternateImage" options: nil];
170 [mediaPlayerPlayPauseStopButton bind:@"image" toObject:mediaPlayer withKeyPath:@"stateAsButtonImage" options: nil];
171 [mediaPlayerBackwardPrevButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
172 [mediaPlayerForwardNextButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
175 [mediaPlayerForwardNextButton setTarget:mediaPlayer];
176 [mediaPlayerForwardNextButton setAction:@selector(fastForward)];
177 [mediaPlayerBackwardPrevButton setTarget:mediaPlayer];
178 [mediaPlayerBackwardPrevButton setAction:@selector(rewind)];
179 [mediaPlayerPlayPauseStopButton setTarget:mediaPlayer];
180 [mediaPlayerPlayPauseStopButton setAction:@selector(pause)];
182 /* Last minute setup */
183 [categoriesListView expandItem:nil expandChildren:YES];
184 [categoriesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[categoriesListView numberOfRows] > 0 ? [categoriesListView numberOfRows]-1 : 0] byExtendingSelection:NO];
185 [self setNavigatorViewVisible:NO animate:NO];
186 [self showWindow:self];
187 [mainSplitView setDelegate:self];
190 - (BOOL)navigatorViewVisible
192 return [mainSplitView sliderPosition] <= [mainSplitView bounds].size.width - [mainSplitView dividerThickness] - 30.f /* To be tolerant */;
195 - (void)setNavigatorViewVisible:(BOOL)wantsVisible animate:(BOOL)animate
197 if( [self navigatorViewVisible] == wantsVisible )
200 if( !animate ) [self willChangeValueForKey:@"navigatorViewVisible"];
202 VLCOneSplitView * splitView = animate ? [mainSplitView animator] : mainSplitView;
206 if( navigatorViewWidth >= [mainSplitView bounds].size.width - 200.f )
207 navigatorViewWidth = [mainSplitView bounds].size.width - 200.f;
208 [splitView setSliderPosition:navigatorViewWidth];
212 navigatorViewWidth = [videoView frame].size.width;
213 [splitView setSliderPosition:[mainSplitView bounds].size.width - [mainSplitView dividerThickness]];
215 if( !animate ) [self didChangeValueForKey:@"navigatorViewVisible"];
218 - (void)setNavigatorViewVisible:(BOOL)wantsVisible
220 [self setNavigatorViewVisible:wantsVisible animate:YES];
223 - (IBAction)mediaListViewItemDoubleClicked:(id)sender
225 if([[mediaArrayController selectedObjects] count] <= 0 )
227 [mediaPlayer setMedia:[[mediaArrayController selectedObjects] objectAtIndex:0]];
231 - (void)videoViewItemClicked:(id)sender
233 id object = [sender selectedObject];
234 NSAssert( [object isKindOfClass:[VLCMedia class]], @"Object is not a VLCMedia" );
236 [mediaPlayer setMedia:object];
242 /******************************************************************************
243 * @implementation VLCMainWindowController (NSToolbarDelegating)
246 @implementation VLCMainWindowController (NSToolbarDelegating)
247 /* Our item identifiers */
248 static NSString * VLCToolbarMediaControl = @"VLCToolbarMediaControl";
249 static NSString * VLCToolbarMediaAudioVolume = @"VLCToolbarMediaAudioVolume";
250 static NSString * VLCToolbarMediaDescription = @"VLCToolbarMediaDescription";
252 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
254 return [NSArray arrayWithObjects:
255 NSToolbarCustomizeToolbarItemIdentifier,
256 NSToolbarFlexibleSpaceItemIdentifier,
257 NSToolbarSpaceItemIdentifier,
258 NSToolbarSeparatorItemIdentifier,
259 VLCToolbarMediaControl,
260 VLCToolbarMediaAudioVolume,
261 VLCToolbarMediaDescription,
265 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
267 return [NSArray arrayWithObjects:
268 VLCToolbarMediaControl,
269 VLCToolbarMediaAudioVolume,
270 VLCToolbarMediaDescription,
274 - (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
276 NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
278 if( [itemIdentifier isEqual: VLCToolbarMediaControl] )
280 [toolbarItem setLabel:@"Media Controls"];
281 [toolbarItem setPaletteLabel:@"Media Controls"];
283 [toolbarItem setView:toolbarMediaControl];
284 [toolbarItem setMinSize:[[toolbarItem view] frame].size];
285 [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
287 /* TODO: setup a menu */
289 else if( [itemIdentifier isEqual: VLCToolbarMediaAudioVolume] )
291 [toolbarItem setLabel:@"Audio Volume"];
292 [toolbarItem setPaletteLabel:@"Audio Volume"];
294 [toolbarItem setView:toolbarMediaAudioVolume];
295 [toolbarItem setMinSize:[[toolbarItem view] frame].size];
296 [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
298 /* TODO: setup a menu */
300 else if( [itemIdentifier isEqual: VLCToolbarMediaDescription] )
302 [toolbarItem setLabel:@"Media Description"];
303 [toolbarItem setPaletteLabel:@"Media Description"];
305 [toolbarItem setView:toolbarMediaDescription];
306 [toolbarItem setMinSize:[[toolbarItem view] frame].size];
307 [toolbarItem setMaxSize:NSMakeSize(10000 /* Can be really big */, NSHeight([[toolbarItem view] frame]))];
309 /* TODO: setup a menu */
313 /* itemIdentifier referred to a toolbar item that is not
314 * provided or supported by us or Cocoa
315 * Returning nil will inform the toolbar
316 * that this kind of item is not supported */
323 /******************************************************************************
324 * VLCMainWindowController (CategoriesListDelegate)
326 @implementation VLCMainWindowController (CategoriesListDelegate)
327 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
329 return [[item representedObject] isKindOfClass:[NSDictionary class]];
331 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
333 return !([[item representedObject] isKindOfClass:[NSDictionary class]]);
335 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
337 [cell setRepresentedObject:[item representedObject]];
341 /******************************************************************************
342 * VLCMainWindowController (CategoriesListDataSource)
344 @implementation VLCMainWindowController (CategoriesListDataSource)
346 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
350 if(![item respondsToSelector:@selector(representedObject)])
353 NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
355 droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
357 droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
359 NSAssert( droppedItems, @"Dropped an unsupported object type on the outline View" );
361 VLCMediaList * mediaList = [(VLCMedia *)[item representedObject] subitems];
363 for (i = 0; i < [droppedItems count]; i++)
365 NSString * filename = [droppedItems objectAtIndex:i];
366 VLCMedia *media = [VLCMedia mediaWithPath:filename];
368 [mediaList insertMedia:media atIndex:index+1];
374 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
376 NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
378 droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
380 droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
383 ![item respondsToSelector:@selector(representedObject)] ||
384 ![[item representedObject] isKindOfClass:[VLCMedia class]] )
386 return NSDragOperationNone;
389 return NSDragOperationMove;
393 /******************************************************************************
394 * VLCMainWindowController (SplitViewDelegate)
396 @implementation VLCMainWindowController (SplitViewDelegate)
398 - (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
400 [self willChangeValueForKey:@"navigatorViewVisible"];
402 - (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
404 [self didChangeValueForKey:@"navigatorViewVisible"];