1 /*****************************************************************************
2 * VLCMainWindowController.m: VLCMainWindowController implementation
3 *****************************************************************************
4 * Copyright (C) 2007 Pierre d'Herbemont
5 * Copyright (C) 2007 the VideoLAN team
6 * $Id: VLCMainWindow.h 24209 2008-01-09 22:05:17Z pdherbemont $
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;
41 NSTableColumn * tableColumn;
43 /***********************************
44 * Init the media player
47 NSAssert( mediaPlayer, @"No mediaPlayer" );
49 categoriesTreeController = [[NSTreeController alloc] init];
51 /***********************************
52 * CategoriesList OutlineView content
54 /* categoriesTreeController */
55 NSAssert( categoriesTreeController, @"No categoriesTreeController" );
56 NSAssert( categoriesListView, @"No categoriesListView" );
57 NSAssert( controller, @"No controller" );
59 [categoriesTreeController setContent:controller.categories];
60 //[categoriesTreeController bind:@"content" toObject:controller withKeyPath:@"categories" options:nil];
62 [categoriesTreeController setChildrenKeyPath:@"childrenInCategoriesList"];
64 /* Bind the "name" table column */
65 tableColumn = [categoriesListView tableColumnWithIdentifier:@"name"];
66 [tableColumn bind:@"value" toObject:categoriesTreeController withKeyPath:@"arrangedObjects.descriptionInCategoriesList" options:nil];
68 /* Use an ImageAndTextCell in the "name" table column */
69 ImageAndTextCell * cell = [[ImageAndTextCell alloc] init];
70 [cell setFont:[[tableColumn dataCell] font]];
71 [cell setImageKeyPath:@"image"];
72 [tableColumn setDataCell: cell];
75 [categoriesListView setIndentationMarkerFollowsCell:YES];
76 [categoriesListView setAutoresizesOutlineColumn:NO];
77 [categoriesListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
78 [categoriesListView setDelegate:self];
80 [categoriesListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, @"VLCMediaURLType", nil]];
81 [categoriesListView setDataSource: self];
83 /***********************************
87 /* 1- Drag and drop */
88 NSAssert( mediaArrayController, @"No mediaArrayController" );
89 NSAssert( mediaListView, @"No mediaListView" );
90 [mediaListView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
91 [mediaListView setDataSource:mediaArrayController];
92 /* 3- binding for "title" column */
93 tableColumn = [mediaListView tableColumnWithIdentifier:@"title"];
94 [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.metaDictionary.title" options:nil];
98 [mediaListView setTarget:self];
99 [mediaListView setDoubleAction:@selector(mediaListViewItemDoubleClicked:)];
102 /* 4- binding for "state" column */
103 tableColumn = [mediaListView tableColumnWithIdentifier:@"state"];
104 [tableColumn bind:@"value" toObject: mediaArrayController withKeyPath:@"arrangedObjects.stateAsImage" options:nil];
106 /* 6- Bind the @"contentArray" and contentMediaList of the mediaArrayController */
107 [mediaArrayController bind:@"contentArray" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.media" options:nil];
109 [mediaArrayController bind:@"contentMediaList" toObject:categoriesTreeController withKeyPath:@"selection.childrenInCategoriesListForDetailView.parentMediaList" options:nil];
112 [mediaListView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
113 [mediaListView setAllowsTypeSelect:YES];
115 /***********************************
118 [videoView setItemsTree:controller.categories];
119 [videoView setNodeKeyPath:@"childrenInVideoView"];
120 [videoView setContentKeyPath:@"descriptionInVideoView"];
121 [videoView setTarget:self];
122 [videoView setAction:@selector(videoViewItemClicked:)];
124 /***********************************
128 /***********************************
129 * Other interface element setup
133 [mediaListItemsCount bind:@"displayPatternValue1" toObject:mediaArrayController withKeyPath:@"arrangedObjects.@count" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
134 [mediaListItemFetchedStatus bind:@"animate" toObject:categoriesTreeController withKeyPath:@"selection.currentlyFetchingItems" options:[NSDictionary dictionaryWithObject:@"%{value1}@ items" forKey:NSDisplayPatternBindingOption]];
136 [fillScreenButton bind:@"value" toObject:videoView withKeyPath:@"fillScreen" options: nil];
137 [fullScreenButton bind:@"value" toObject:videoView withKeyPath:@"fullScreen" options: nil];
138 [fullScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
139 [fillScreenButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
140 [mediaReadingProgressSlider bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
141 [mediaReadingProgressSlider bind:@"enabled2" toObject:mediaPlayer withKeyPath:@"seekable" options: nil];
143 [mediaReadingProgressSlider bind:@"value" toObject:mediaPlayer withKeyPath:@"position" options:
144 [NSDictionary dictionaryWithObjectsAndKeys:@"Float10000FoldTransformer", NSValueTransformerNameBindingOption,
145 [NSNumber numberWithBool:NO], NSConditionallySetsEnabledBindingOption, nil ]];
146 [mediaReadingProgressText bind:@"value" toObject:mediaPlayer withKeyPath:@"time.stringValue" options: nil];
147 [mediaDescriptionText bind:@"value" toObject:mediaPlayer withKeyPath:@"description" options: nil];
148 [self bind:@"representedFilename" toObject:mediaPlayer withKeyPath:@"media.url" options: [NSDictionary dictionaryWithObject:@"URLToRepresentedFileNameTransformer" forKey:NSValueTransformerNameBindingOption]];
149 [self bind:@"title" toObject:mediaPlayer withKeyPath:@"description" options: nil];
151 [navigatorViewToggleButton bind:@"value" toObject:self withKeyPath:@"navigatorViewVisible" options: nil];
154 /* Playlist buttons */
156 [removePlaylistButton bind:@"enabled" toObject:categoriesTreeController withKeyPath:@"selection.editableInCategoriesList" options: nil];
158 [removePlaylistButton setTarget:categoriesTreeController];
159 [removePlaylistButton setAction:@selector(remove:)];
160 [addPlaylistButton setTarget:controller];
161 [addPlaylistButton setAction:@selector(addPlaylist:)];
165 [mediaPlayerPlayPauseStopButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"media" options: [NSDictionary dictionaryWithObject:@"NonNilAsBoolTransformer" forKey:NSValueTransformerNameBindingOption]];
166 [mediaPlayerPlayPauseStopButton bind:@"state" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
167 [mediaPlayerPlayPauseStopButton bind:@"alternateImage" toObject:mediaPlayer withKeyPath:@"stateAsButtonAlternateImage" options: nil];
168 [mediaPlayerPlayPauseStopButton bind:@"image" toObject:mediaPlayer withKeyPath:@"stateAsButtonImage" options: nil];
169 [mediaPlayerBackwardPrevButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
170 [mediaPlayerForwardNextButton bind:@"enabled" toObject:mediaPlayer withKeyPath:@"playing" options: nil];
173 [mediaPlayerForwardNextButton setTarget:mediaPlayer];
174 [mediaPlayerForwardNextButton setAction:@selector(fastForward)];
175 [mediaPlayerBackwardPrevButton setTarget:mediaPlayer];
176 [mediaPlayerBackwardPrevButton setAction:@selector(rewind)];
177 [mediaPlayerPlayPauseStopButton setTarget:mediaPlayer];
178 [mediaPlayerPlayPauseStopButton setAction:@selector(pause)];
180 /* Last minute setup */
181 [categoriesListView expandItem:nil expandChildren:YES];
182 [categoriesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[categoriesListView numberOfRows] > 0 ? [categoriesListView numberOfRows]-1 : 0] byExtendingSelection:NO];
183 [self setNavigatorViewVisible:NO animate:NO];
184 [self showWindow:self];
185 [mainSplitView setDelegate:self];
188 - (BOOL)navigatorViewVisible
190 return [mainSplitView sliderPosition] <= [mainSplitView bounds].size.width - [mainSplitView dividerThickness] - 30.f /* To be tolerant */;
193 - (void)setNavigatorViewVisible:(BOOL)wantsVisible animate:(BOOL)animate
195 if( [self navigatorViewVisible] == wantsVisible )
198 if( !animate ) [self willChangeValueForKey:@"navigatorViewVisible"];
200 VLCOneSplitView * splitView = animate ? [mainSplitView animator] : mainSplitView;
204 if( navigatorViewWidth >= [mainSplitView bounds].size.width - 200.f )
205 navigatorViewWidth = [mainSplitView bounds].size.width - 200.f;
206 [splitView setSliderPosition:navigatorViewWidth];
210 navigatorViewWidth = [videoView frame].size.width;
211 [splitView setSliderPosition:[mainSplitView bounds].size.width - [mainSplitView dividerThickness]];
213 if( !animate ) [self didChangeValueForKey:@"navigatorViewVisible"];
216 - (void)setNavigatorViewVisible:(BOOL)wantsVisible
218 [self setNavigatorViewVisible:wantsVisible animate:YES];
221 - (IBAction)mediaListViewItemDoubleClicked:(id)sender
223 if([[mediaArrayController selectedObjects] count] <= 0 )
225 [mediaPlayer setMedia:[[mediaArrayController selectedObjects] objectAtIndex:0]];
229 - (void)videoViewItemClicked:(id)sender
231 id object = [sender selectedObject];
232 NSAssert( [object isKindOfClass:[VLCMedia class]], @"Object is not a VLCMedia" );
234 [mediaPlayer setMedia:object];
240 /******************************************************************************
241 * @implementation VLCMainWindowController (NSToolbarDelegating)
244 @implementation VLCMainWindowController (NSToolbarDelegating)
245 /* Our item identifiers */
246 static NSString * VLCToolbarMediaControl = @"VLCToolbarMediaControl";
247 static NSString * VLCToolbarMediaAudioVolume = @"VLCToolbarMediaAudioVolume";
248 static NSString * VLCToolbarMediaDescription = @"VLCToolbarMediaDescription";
250 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
252 return [NSArray arrayWithObjects:
253 NSToolbarCustomizeToolbarItemIdentifier,
254 NSToolbarFlexibleSpaceItemIdentifier,
255 NSToolbarSpaceItemIdentifier,
256 NSToolbarSeparatorItemIdentifier,
257 VLCToolbarMediaControl,
258 VLCToolbarMediaAudioVolume,
259 VLCToolbarMediaDescription,
263 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
265 return [NSArray arrayWithObjects:
266 VLCToolbarMediaControl,
267 VLCToolbarMediaAudioVolume,
268 VLCToolbarMediaDescription,
272 - (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
274 NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
276 if( [itemIdentifier isEqual: VLCToolbarMediaControl] )
278 [toolbarItem setLabel:@"Media Controls"];
279 [toolbarItem setPaletteLabel:@"Media Controls"];
281 [toolbarItem setView:toolbarMediaControl];
282 [toolbarItem setMinSize:[[toolbarItem view] frame].size];
283 [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
285 /* TODO: setup a menu */
287 else if( [itemIdentifier isEqual: VLCToolbarMediaAudioVolume] )
289 [toolbarItem setLabel:@"Audio Volume"];
290 [toolbarItem setPaletteLabel:@"Audio Volume"];
292 [toolbarItem setView:toolbarMediaAudioVolume];
293 [toolbarItem setMinSize:[[toolbarItem view] frame].size];
294 [toolbarItem setMaxSize:[[toolbarItem view] frame].size];
296 /* TODO: setup a menu */
298 else if( [itemIdentifier isEqual: VLCToolbarMediaDescription] )
300 [toolbarItem setLabel:@"Media Description"];
301 [toolbarItem setPaletteLabel:@"Media Description"];
303 [toolbarItem setView:toolbarMediaDescription];
304 [toolbarItem setMinSize:[[toolbarItem view] frame].size];
305 [toolbarItem setMaxSize:NSMakeSize(10000 /* Can be really big */, NSHeight([[toolbarItem view] frame]))];
307 /* TODO: setup a menu */
311 /* itemIdentifier referred to a toolbar item that is not
312 * provided or supported by us or Cocoa
313 * Returning nil will inform the toolbar
314 * that this kind of item is not supported */
321 /******************************************************************************
322 * VLCMainWindowController (CategoriesListDelegate)
324 @implementation VLCMainWindowController (CategoriesListDelegate)
325 - (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
327 return [[item representedObject] isKindOfClass:[NSDictionary class]];
329 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
331 return !([[item representedObject] isKindOfClass:[NSDictionary class]]);
333 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
335 [cell setRepresentedObject:[item representedObject]];
339 /******************************************************************************
340 * VLCMainWindowController (CategoriesListDataSource)
342 @implementation VLCMainWindowController (CategoriesListDataSource)
344 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
348 if(![item respondsToSelector:@selector(representedObject)])
351 NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
353 droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
355 droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
357 NSAssert( droppedItems, @"Dropped an unsupported object type on the outline View" );
359 VLCMediaList * mediaList = [(VLCMedia *)[item representedObject] subitems];
361 for (i = 0; i < [droppedItems count]; i++)
363 NSString * filename = [droppedItems objectAtIndex:i];
364 VLCMedia *media = [VLCMedia mediaWithPath:filename];
366 [mediaList insertMedia:media atIndex:index+1];
372 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
374 NSArray *droppedItems = [[info draggingPasteboard] propertyListForType:@"VLCMediaURLType"];
376 droppedItems = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
378 droppedItems = [[info draggingPasteboard] propertyListForType:NSURLPboardType];
381 ![item respondsToSelector:@selector(representedObject)] ||
382 ![[item representedObject] isKindOfClass:[VLCMedia class]] )
384 return NSDragOperationNone;
387 return NSDragOperationMove;
391 /******************************************************************************
392 * VLCMainWindowController (SplitViewDelegate)
394 @implementation VLCMainWindowController (SplitViewDelegate)
396 - (void)splitViewWillResizeSubviews:(NSNotification *)aNotification
398 [self willChangeValueForKey:@"navigatorViewVisible"];
400 - (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
402 [self didChangeValueForKey:@"navigatorViewVisible"];