VLCMainWindowController.m 17.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*****************************************************************************
 * 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;
38
@synthesize categoriesTreeController;
39
40
41
42
43
44
45
46
47
48

- (void)awakeFromNib
{
    NSTableColumn * tableColumn;

    /***********************************
     * Init the media player
     */

    NSAssert( mediaPlayer, @"No mediaPlayer" );
49
    [self willChangeValueForKey:@"categoriesTreeController"];
50
    categoriesTreeController = [[NSTreeController alloc] init];
51
52
    [self didChangeValueForKey:@"categoriesTreeController"];

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    /***********************************
     * 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