]> git.sesse.net Git - vlc/blob - modules/gui/macosx/ConvertAndSave.m
macosx: CAS: re-write the destination section's appearance to make it less cluttered
[vlc] / modules / gui / macosx / ConvertAndSave.m
1 /*****************************************************************************
2  * ConvertAndSave.h: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2012 Felix Paul Kühne
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #import "ConvertAndSave.h"
25 #import "intf.h"
26 #import "playlist.h"
27 #import <vlc_common.h>
28 #import <vlc_url.h>
29
30 /* mini doc:
31  * the used NSMatrix includes a bunch of cells referenced most easily by tags. There you go: */
32 #define MPEGTS 0
33 #define WEBM 1
34 #define OGG 2
35 #define MP4 3
36 #define MPEGPS 4
37 #define MJPEG 5
38 #define WAV 6
39 #define FLV 7
40 #define MPEG1 8
41 #define MKV 9
42 #define RAW 10
43 #define AVI 11
44 #define ASF 12
45 /* 13-15 are present, but not set */
46
47 @implementation VLCConvertAndSave
48
49 @synthesize MRL=_MRL, outputDestination=_outputDestination, profileNames=_profileNames, profileValueList=_profileValueList, currentProfile=_currentProfile;
50
51 static VLCConvertAndSave *_o_sharedInstance = nil;
52
53 #pragma mark -
54 #pragma mark Initialization
55
56 + (void)initialize{
57     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
58
59     /* We are using the same format as the Qt4 intf here:
60      * Container(string), transcode video(bool), transcode audio(bool),
61      * use subtitles(bool), video codec(string), video bitrate(integer),
62      * scale(float), fps(float), width(integer, height(integer),
63      * audio codec(string), audio bitrate(integer), channels(integer),
64      * samplerate(integer), subtitle codec(string), subtitle overlay(bool) */
65     NSArray * defaultProfiles = [[NSArray alloc] initWithObjects:
66                                  @"mp4;1;1;0;h264;0;0;0;0;0;mpga;128;2;44100;0;1",
67                                  @"webm;1;1;0;VP80;2000;0;0;0;0;vorb;128;2;44100;0;1",
68                                  @"ts;1;1;0;h264;800;1;0;0;0;mpga;128;2;44100;0;0",
69                                  @"ts;1;1;0;drac;800;1;0;0;0;mpga;128;2;44100;0;0",
70                                  @"ogg;1;1;0;theo;800;1;0;0;0;vorb;128;2;44100;0;0",
71                                  @"ogg;1;1;0;theo;800;1;0;0;0;flac;128;2;44100;0;0",
72                                  @"ts;1;1;0;mp2v;800;1;0;0;0;mpga;128;2;44100;0;0",
73                                  @"asf;1;1;0;WMV2;800;1;0;0;0;wma2;128;2;44100;0;0",
74                                  @"asf;1;1;0;DIV3;800;1;0;0;0;mp3;128;2;44100;0;0",
75                                  @"ogg;1;1;0;none;800;1;0;0;0;vorb;128;2;44100;none;0",
76                                  @"raw;1;1;0;none;800;1;0;0;0;mp3;128;2;44100;none;0",
77                                  @"mp4;1;1;0;none;800;1;0;0;0;mpga;128;2;44100;none;0",
78                                  @"raw;1;1;0;none;800;1;0;0;0;flac;128;2;44100;none;0",
79                                  @"wav;1;1;0;none;800;1;0;0;0;s16l;128;2;44100;none;0", nil];
80
81     NSArray * defaultProfileNames = [[NSArray alloc] initWithObjects:
82                                      @"Video - H.264 + MP3 (MP4)",
83                                      @"Video - VP80 + Vorbis (Webm)",
84                                      @"Video - H.264 + MP3 (TS)",
85                                      @"Video - Dirac + MP3 (TS)",
86                                      @"Video - Theora + Vorbis (OGG)",
87                                      @"Video - Theora + Flac (OGG)",
88                                      @"Video - MPEG-2 + MPGA (TS)",
89                                      @"Video - WMV + WMA (ASF)",
90                                      @"Video - DIV3 + MP3 (ASF)",
91                                      @"Audio - Vorbis (OGG)",
92                                      @"Audio - MP3",
93                                      @"Audio - MP3 (MP4)",
94                                      @"Audio - FLAC",
95                                      @"Audio - CD",
96                                      nil];
97
98     NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:defaultProfiles, @"CASProfiles", defaultProfileNames, @"CASProfileNames", nil];
99
100     [defaults registerDefaults:appDefaults];
101     [defaultProfiles release];
102 }
103
104 + (VLCConvertAndSave *)sharedInstance
105 {
106     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
107 }
108
109 - (id)init
110 {
111     if (_o_sharedInstance) {
112         [self dealloc];
113     } else {
114         _o_sharedInstance = [super init];
115     }
116
117     return _o_sharedInstance;
118 }
119
120 - (void)dealloc
121 {
122     if (_currentProfile)
123         [_currentProfile release];
124
125     [_profileNames release];
126     [_profileValueList release];
127     [_videoCodecs release];
128     [_audioCodecs release];
129     [_subsCodecs release];
130
131     [super dealloc];
132 }
133
134 - (void)awakeFromNib
135 {
136     [_window setTitle: _NS("Convert & Save")];
137     [_cancel_btn setTitle: _NS("Cancel")];
138     [_ok_btn setTitle: _NS("Save")];
139     [_drop_lbl setStringValue: _NS("Drop media here")];
140     [_drop_btn setTitle: _NS("Open media...")];
141     [_profile_lbl setStringValue: _NS("Choose Profile")];
142     [_profile_btn setTitle: _NS("Customize")];
143     [_destination_lbl setStringValue: _NS("Choose Destination")];
144     [_destination_filename_stub_lbl setStringValue: _NS("Choose an output location")];
145     [_destination_filename_lbl setHidden: YES];
146     [_destination_browse_btn setTitle:_NS("Browse...")];
147     [_destination_stream_btn setTitle:_NS("Setup Streaming...")];
148     [_destination_stream_lbl setStringValue:@""];
149     [_destination_itwantafile_btn setTitle:_NS("Save as File")];
150     [_destination_itwantastream_btn setTitle:_NS("Stream")];
151     [_destination_cancel_btn setHidden:YES];
152     [_customize_ok_btn setTitle: _NS("Apply")];
153     [_customize_cancel_btn setTitle: _NS("Cancel")];
154     [[_customize_tabview tabViewItemAtIndex:0] setLabel: _NS("Encapsulation")];
155     [[_customize_tabview tabViewItemAtIndex:1] setLabel: _NS("Video codec")];
156     [[_customize_tabview tabViewItemAtIndex:2] setLabel: _NS("Audio codec")];
157     [[_customize_tabview tabViewItemAtIndex:3] setLabel: _NS("Subtitles")];
158     [_customize_tabview selectTabViewItemAtIndex: 0];
159     [_customize_vid_ckb setTitle: _NS("Video")];
160     [_customize_vid_keep_ckb setTitle: _NS("Keep original video track")];
161     [_customize_vid_codec_lbl setStringValue: _NS("Codec")];
162     [_customize_vid_bitrate_lbl setStringValue: _NS("Bitrate")];
163     [_customize_vid_framerate_lbl setStringValue: _NS("Frame Rate")];
164     [_customize_vid_res_box setTitle: _NS("Resolution")];
165     [_customize_vid_res_lbl setStringValue: _NS("You just need to fill one of the three following parameters, VLC will autodetect the other using the original aspect ratio")];
166     [_customize_vid_width_lbl setStringValue: _NS("Width")];
167     [_customize_vid_height_lbl setStringValue: _NS("Height")];
168     [_customize_vid_scale_lbl setStringValue: _NS("Scale")];
169     [_customize_aud_ckb setTitle: _NS("Audio")];
170     [_customize_aud_keep_ckb setTitle: _NS("Keep original audio track")];
171     [_customize_aud_codec_lbl setStringValue: _NS("Codec")];
172     [_customize_aud_bitrate_lbl setStringValue: _NS("Bitrate")];
173     [_customize_aud_channels_lbl setStringValue: _NS("Channels")];
174     [_customize_aud_samplerate_lbl setStringValue: _NS("Sample Rate")];
175     [_customize_subs_ckb setTitle: _NS("Subtitles")];
176     [_customize_subs_overlay_ckb setTitle: _NS("Overlay subtitles on the video")];
177     [_stream_ok_btn setTitle:_NS("Set")];
178     [_stream_cancel_btn setTitle:_NS("Cancel")];
179     [_stream_destination_lbl setStringValue:_NS("Stream Destination")];
180     [_stream_announcement_lbl setStringValue:_NS("Stream Announcement")];
181     [_stream_type_lbl setStringValue:_NS("Type")];
182     [_stream_address_lbl setStringValue:_NS("Address")];
183     [_stream_ttl_lbl setStringValue:_NS("TTL")];
184     [_stream_port_lbl setStringValue:_NS("Port")];
185     [_stream_sap_ckb setStringValue:_NS("SAP Announcement")];
186     [_stream_http_ckb setStringValue:_NS("HTTP Announcement")];
187     [_stream_rtsp_ckb setStringValue:_NS("RTSP Announcement")];
188     [_stream_sdp_ckb setStringValue:_NS("Export SDP as file")];
189
190     /* there is no way to hide single cells, so replace the existing ones with empty cells.. */
191     id blankCell = [[[NSCell alloc] init] autorelease];
192     [blankCell setEnabled:NO];
193     [_customize_encap_matrix putCell:blankCell atRow:3 column:1];
194     [_customize_encap_matrix putCell:blankCell atRow:3 column:2];
195     [_customize_encap_matrix putCell:blankCell atRow:3 column:3];
196
197     /* fetch profiles from defaults */
198     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
199     _profileValueList = [[defaults arrayForKey:@"CASProfiles"] retain];
200     _profileNames = [[defaults arrayForKey:@"CASProfileNames"] retain];
201
202     [_profile_pop removeAllItems];
203     [_profile_pop addItemsWithTitles:_profileNames];
204     [_profile_pop addItemWithTitle:_NS("Custom")];
205
206     _videoCodecs = [[NSArray alloc] initWithObjects:
207                     [NSArray arrayWithObjects:@"MPEG-1", @"MPEG-2", @"MPEG-4", @"DIVX 1", @"DIVX 2", @"DIVX 3", @"H.263", @"H.264", @"VP8", @"WMV1", @"WMV2", @"M-JPEG", @"Theora", @"Dirac", nil],
208                     [NSArray arrayWithObjects:@"mpgv", @"mp2v", @"mp4v", @"DIV1", @"DIV2", @"DIV3", @"H263", @"h264", @"VP80", @"WMV1", @"WMV2", @"MJPG", @"theo", @"drac", nil],
209                     nil];
210     _audioCodecs = [[NSArray alloc] initWithObjects:
211                     [NSArray arrayWithObjects:@"MPEG Audio", @"MP3", @"MPEG 4 Audio (AAC)", @"A52/AC-3", @"Vorbis", @"Flac", @"Speex", @"WAV", @"WMA2", nil],
212                     [NSArray arrayWithObjects:@"mpga", @"mp3", @"mp4a", @"a52", @"vorb", @"flac", @"spx", @"s16l", @"wma2", nil],
213                     nil];
214     _subsCodecs = [[NSArray alloc] initWithObjects:
215                    [NSArray arrayWithObjects:@"DVB subtitle", @"T.140", nil],
216                    [NSArray arrayWithObjects:@"dvbs", @"t140", nil],
217                    nil];
218
219     [_customize_vid_codec_pop removeAllItems];
220     [_customize_vid_scale_pop removeAllItems];
221     [_customize_aud_codec_pop removeAllItems];
222     [_customize_aud_samplerate_pop removeAllItems];
223     [_customize_subs_pop removeAllItems];
224
225     [_customize_vid_codec_pop addItemsWithTitles:[_videoCodecs objectAtIndex:0]];
226     [_customize_aud_codec_pop addItemsWithTitles:[_audioCodecs objectAtIndex:0]];
227     [_customize_subs_pop addItemsWithTitles:[_subsCodecs objectAtIndex:0]];
228
229     [_customize_aud_samplerate_pop addItemWithTitle:@"8000"];
230     [_customize_aud_samplerate_pop addItemWithTitle:@"11025"];
231     [_customize_aud_samplerate_pop addItemWithTitle:@"22050"];
232     [_customize_aud_samplerate_pop addItemWithTitle:@"44100"];
233     [_customize_aud_samplerate_pop addItemWithTitle:@"48000"];
234
235     [_customize_vid_scale_pop addItemWithTitle:@"1"];
236     [_customize_vid_scale_pop addItemWithTitle:@"0.25"];
237     [_customize_vid_scale_pop addItemWithTitle:@"0.5"];
238     [_customize_vid_scale_pop addItemWithTitle:@"0.75"];
239     [_customize_vid_scale_pop addItemWithTitle:@"1.25"];
240     [_customize_vid_scale_pop addItemWithTitle:@"1.5"];
241     [_customize_vid_scale_pop addItemWithTitle:@"1.75"];
242     [_customize_vid_scale_pop addItemWithTitle:@"2"];
243
244     [_ok_btn setEnabled: NO];
245
246     [self resetCustomizationSheetBasedOnProfile:[_profileValueList objectAtIndex:0]];
247 }
248
249 # pragma mark -
250 # pragma mark Code to Communicate with other objects
251
252 - (void)toggleWindow
253 {
254     [_window makeKeyAndOrderFront: nil];
255 }
256
257 # pragma mark -
258 # pragma mark User Interaction
259
260 - (IBAction)saveFile:(id)sender
261 {
262     playlist_t * p_playlist = pl_Get(VLCIntf);
263
264     input_item_t *p_input = input_item_New([_MRL UTF8String], [[_dropin_media_lbl stringValue] UTF8String]);
265     if (!p_input)
266         return;
267
268     input_item_AddOption(p_input, [[self composedOptions] UTF8String], VLC_INPUT_OPTION_TRUSTED);
269
270     int returnValue;
271     returnValue = playlist_AddInput( p_playlist, p_input, PLAYLIST_STOP, PLAYLIST_END, true, pl_Unlocked );
272
273     if (returnValue == VLC_SUCCESS) {
274         /* let's "play" */
275         PL_LOCK;
276         playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input );
277         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, NULL,
278                          p_item );
279         PL_UNLOCK;
280     }
281     else
282         msg_Err( VLCIntf, "CAS: playlist add input failed :(");
283
284     /* we're done with this input */
285     vlc_gc_decref( p_input );
286
287     [_window performClose:sender];
288 }
289
290 - (IBAction)openMedia:(id)sender
291 {
292     /* preliminary implementation until the open panel is cleaned up */
293     NSOpenPanel * openPanel = [NSOpenPanel openPanel];
294     [openPanel setCanChooseDirectories:NO];
295     [openPanel setResolvesAliases:YES];
296     [openPanel setAllowsMultipleSelection:NO];
297     [openPanel beginSheetForDirectory:nil file:nil types:nil modalForWindow: _window modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
298 }
299
300 - (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode  contextInfo:(void  *)contextInfo
301 {
302     if (returnCode == NSOKButton)
303     {
304         [self setMRL: [NSString stringWithUTF8String:make_URI([[[panel URL] path] UTF8String], NULL)]];
305         [self updateOKButton];
306         [self updateDropView];
307     }
308 }
309
310 - (IBAction)switchProfile:(id)sender
311 {
312     NSUInteger index = [_profile_pop indexOfSelectedItem];
313     if (index < ([_profileValueList count] - 1))
314         [self resetCustomizationSheetBasedOnProfile:[_profileValueList objectAtIndex:index]];
315 }
316
317 - (IBAction)customizeProfile:(id)sender
318 {
319     [NSApp beginSheet:_customize_panel modalForWindow:_window modalDelegate:self didEndSelector:NULL contextInfo:nil];
320 }
321
322 - (IBAction)closeCustomizationSheet:(id)sender
323 {
324     [_customize_panel orderOut:sender];
325     [NSApp endSheet: _customize_panel];
326
327     /* update current profile based upon the sheet's values */
328     /* Container(string), transcode video(bool), transcode audio(bool),
329      * use subtitles(bool), video codec(string), video bitrate(integer),
330      * scale(float), fps(float), width(integer, height(integer),
331      * audio codec(string), audio bitrate(integer), channels(integer),
332      * samplerate(integer), subtitle codec(string), subtitle overlay(bool) */
333
334     if (sender == _customize_ok_btn && [_currentProfile count] == 16) {
335         NSInteger i;
336         [_currentProfile replaceObjectAtIndex:0 withObject:[self currentEncapsulationFormatAsFileExtension:NO]];
337         [_currentProfile replaceObjectAtIndex:1 withObject:[NSString stringWithFormat:@"%li", [_customize_vid_ckb state]]];
338         [_currentProfile replaceObjectAtIndex:2 withObject:[NSString stringWithFormat:@"%li", [_customize_aud_ckb state]]];
339         [_currentProfile replaceObjectAtIndex:3 withObject:[NSString stringWithFormat:@"%li", [_customize_subs_ckb state]]];
340         i = [_customize_vid_codec_pop indexOfSelectedItem];
341         if (i >= 0)
342             [_currentProfile replaceObjectAtIndex:4 withObject:[_videoCodecs objectAtIndex:i]];
343         else
344             [_currentProfile replaceObjectAtIndex:4 withObject:@"none"];
345         [_currentProfile replaceObjectAtIndex:5 withObject:[_customize_vid_bitrate_fld stringValue]];
346         [_currentProfile replaceObjectAtIndex:6 withObject:[[_customize_vid_scale_pop selectedItem] title]];
347         [_currentProfile replaceObjectAtIndex:7 withObject:[_customize_vid_framerate_fld stringValue]];
348         [_currentProfile replaceObjectAtIndex:8 withObject:[_customize_vid_width_fld stringValue]];
349         [_currentProfile replaceObjectAtIndex:9 withObject:[_customize_vid_height_fld stringValue]];
350         i = [_customize_aud_codec_pop indexOfSelectedItem];
351         if (i >= 0)
352             [_currentProfile replaceObjectAtIndex:10 withObject:[_audioCodecs objectAtIndex:i]];
353         else
354             [_currentProfile replaceObjectAtIndex:10 withObject:@"none"];
355         [_currentProfile replaceObjectAtIndex:11 withObject:[_customize_aud_bitrate_fld stringValue]];
356         [_currentProfile replaceObjectAtIndex:12 withObject:[_customize_aud_channels_fld stringValue]];
357         [_currentProfile replaceObjectAtIndex:13 withObject:[[_customize_aud_samplerate_pop selectedItem] title]];
358         i = [_customize_subs_pop indexOfSelectedItem];
359         if (i >= 0)
360             [_currentProfile replaceObjectAtIndex:14 withObject:[_subsCodecs objectAtIndex:i]];
361         else
362             [_currentProfile replaceObjectAtIndex:14 withObject:@"none"];
363         [_currentProfile replaceObjectAtIndex:15 withObject:[NSString stringWithFormat:@"%li", [_customize_subs_overlay_ckb state]]];
364     }
365 }
366
367 - (IBAction)iWantAFile:(id)sender
368 {
369     NSRect boxFrame = [_destination_box frame];
370     NSRect subViewFrame = [_destination_itwantafile_view frame];
371     subViewFrame.origin.x = (boxFrame.size.width - subViewFrame.size.width) / 2;
372     subViewFrame.origin.y = ((boxFrame.size.height - subViewFrame.size.height) / 2) - 15.;
373     [_destination_itwantafile_view setFrame: subViewFrame];
374     [[_destination_itwantafile_btn animator] setHidden: YES];
375     [[_destination_itwantastream_btn animator] setHidden: YES];
376     [_destination_box performSelector:@selector(addSubview:) withObject:_destination_itwantafile_view afterDelay:0.2];
377     [[_destination_cancel_btn animator] setHidden:NO];
378 }
379
380 - (IBAction)iWantAStream:(id)sender
381 {
382     NSRect boxFrame = [_destination_box frame];
383     NSRect subViewFrame = [_destination_itwantastream_view frame];
384     subViewFrame.origin.x = (boxFrame.size.width - subViewFrame.size.width) / 2;
385     subViewFrame.origin.y = ((boxFrame.size.height - subViewFrame.size.height) / 2) - 15.;
386     [_destination_itwantastream_view setFrame: subViewFrame];
387     [[_destination_itwantafile_btn animator] setHidden: YES];
388     [[_destination_itwantastream_btn animator] setHidden: YES];
389     [_destination_box performSelector:@selector(addSubview:) withObject:_destination_itwantastream_view afterDelay:0.2];
390     [[_destination_cancel_btn animator] setHidden:NO];
391 }
392
393 - (IBAction)cancelDestination:(id)sender
394 {
395     if ([_destination_itwantastream_view superview] != nil)
396         [_destination_itwantastream_view removeFromSuperview];
397     if ([_destination_itwantafile_view superview] != nil)
398         [_destination_itwantafile_view removeFromSuperview];
399
400     [_destination_cancel_btn setHidden:YES];
401     [[_destination_itwantafile_btn animator] setHidden: NO];
402     [[_destination_itwantastream_btn animator] setHidden: NO];
403 }
404
405 - (IBAction)browseFileDestination:(id)sender
406 {
407     NSSavePanel * saveFilePanel = [NSSavePanel savePanel];
408     [saveFilePanel setCanSelectHiddenExtension: YES];
409     [saveFilePanel setCanCreateDirectories: YES];
410     if ([[_customize_encap_matrix selectedCell] tag] != RAW) // there is no clever guess for this
411         [saveFilePanel setAllowedFileTypes:[NSArray arrayWithObject:[self currentEncapsulationFormatAsFileExtension:YES]]];
412     [saveFilePanel beginSheetForDirectory:nil file:nil modalForWindow:_window modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
413 }
414
415 - (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
416 {
417     if (returnCode == NSOKButton) {
418         [self setOutputDestination:[[sheet URL] path]];
419         [_destination_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:_outputDestination]];
420         [[_destination_filename_stub_lbl animator] setHidden: YES];
421         [[_destination_filename_lbl animator] setHidden: NO];
422     } else {
423         [self setOutputDestination:@""];
424         [[_destination_filename_lbl animator] setHidden: YES];
425         [[_destination_filename_stub_lbl animator] setHidden: NO];
426     }
427     [self updateOKButton];
428 }
429
430 - (IBAction)showStreamPanel:(id)sender
431 {
432     [NSApp beginSheet:_stream_panel modalForWindow:_window modalDelegate:self didEndSelector:NULL contextInfo:nil];
433 }
434
435 - (IBAction)closeStreamPanel:(id)sender
436 {
437     [_stream_panel orderOut:sender];
438     [NSApp endSheet: _stream_panel];
439 }
440
441 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
442 {
443     NSPasteboard *paste = [sender draggingPasteboard];
444     NSArray *types = [NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil];
445     NSString *desired_type = [paste availableTypeFromArray: types];
446     NSData *carried_data = [paste dataForType: desired_type];
447
448     if( carried_data ) {
449         if( [desired_type isEqualToString:NSFilenamesPboardType] ) {
450             NSArray *values = [[paste propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
451
452             if ([values count] > 0) {
453                 [self setMRL: [NSString stringWithUTF8String:make_URI([[values objectAtIndex:0] UTF8String], NULL)]];
454                 [self updateOKButton];
455                 [self updateDropView];
456                 return YES;
457             }
458         } else if( [desired_type isEqualToString:@"VLCPlaylistItemPboardType"] ) {
459             NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
460             NSUInteger count = [array count];
461             if (count > 0) {
462                 playlist_t * p_playlist = pl_Get( VLCIntf );
463                 playlist_item_t * p_item = NULL;
464
465                 PL_LOCK;
466                 /* let's look for the first proper input item */
467                 for (NSUInteger x = 0; x < count; x++) {
468                     p_item = [[array objectAtIndex:x] pointerValue];
469                     if (p_item) {
470                         if (p_item->p_input) {
471                             if (p_item->p_input->psz_uri != nil) {
472                                 [self setMRL: [NSString stringWithFormat:@"%s", p_item->p_input->psz_uri]];
473                                 [self updateDropView];
474                                 [self updateOKButton];
475
476                                 PL_UNLOCK;
477
478                                 return YES;
479                             }
480                         }
481                     }
482                 }
483                 PL_UNLOCK;
484             }
485         }
486     }
487     return NO;
488 }
489
490 # pragma mark -
491 # pragma mark Private Functionality
492 - (void)updateDropView
493 {
494     if ([_MRL length] > 0) {
495         NSString * path = [[NSURL URLWithString:_MRL] path];
496         [_dropin_media_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: path]];
497         NSImage * image = [[NSWorkspace sharedWorkspace] iconForFile: path];
498         [image setSize:NSMakeSize(128,128)];
499         [_dropin_icon_view setImage: image];
500
501         if (![_dropin_view superview]) {
502             NSRect boxFrame = [_drop_box frame];
503             NSRect subViewFrame = [_dropin_view frame];
504             subViewFrame.origin.x = (boxFrame.size.width - subViewFrame.size.width) / 2;
505             subViewFrame.origin.y = (boxFrame.size.height - subViewFrame.size.height) / 2;
506             [_dropin_view setFrame: subViewFrame];
507             [[_drop_image_view animator] setHidden: YES];
508             [_drop_box performSelector:@selector(addSubview:) withObject:_dropin_view afterDelay:0.6];
509         }
510     } else {
511         [_dropin_view removeFromSuperview];
512         [[_drop_image_view animator] setHidden: NO];
513     }
514 }
515
516 - (void)updateOKButton
517 {
518     if ([_outputDestination length] > 0 && [_MRL length] > 0)
519         [_ok_btn setEnabled: YES];
520     else
521         [_ok_btn setEnabled: NO];
522 }
523
524 - (void)resetCustomizationSheetBasedOnProfile:(NSString *)profileString
525 {
526     /* Container(string), transcode video(bool), transcode audio(bool),
527     * use subtitles(bool), video codec(string), video bitrate(integer),
528     * scale(float), fps(float), width(integer, height(integer),
529     * audio codec(string), audio bitrate(integer), channels(integer),
530     * samplerate(integer), subtitle codec(string), subtitle overlay(bool) */
531
532     NSArray * components = [profileString componentsSeparatedByString:@";"];
533     if ([components count] != 16) {
534         msg_Err(VLCIntf, "CAS: the requested profile '%s' is invalid", [profileString UTF8String]);
535         return;
536     }
537
538     [self selectCellByEncapsulationFormat:[components objectAtIndex:0]];
539     [_customize_vid_ckb setState:[[components objectAtIndex:1] intValue]];
540     [_customize_aud_ckb setState:[[components objectAtIndex:2] intValue]];
541     [_customize_subs_ckb setState:[[components objectAtIndex:3] intValue]];
542     [_customize_vid_bitrate_fld setStringValue:[components objectAtIndex:5]];
543     [_customize_vid_scale_pop selectItemWithTitle:[components objectAtIndex:6]];
544     [_customize_vid_framerate_fld setStringValue:[components objectAtIndex:7]];
545     [_customize_vid_width_fld setStringValue:[components objectAtIndex:8]];
546     [_customize_vid_height_fld setStringValue:[components objectAtIndex:9]];
547     [_customize_aud_bitrate_fld setStringValue:[components objectAtIndex:11]];
548     [_customize_aud_channels_fld setStringValue:[components objectAtIndex:12]];
549     [_customize_aud_samplerate_pop selectItemWithTitle:[components objectAtIndex:13]];
550     [_customize_subs_overlay_ckb setState:[[components objectAtIndex:15] intValue]];
551
552     /* since there is no proper lookup mechanism in arrays, we need to implement a string specific one ourselves */
553     NSArray * tempArray = [_videoCodecs objectAtIndex:1];
554     NSUInteger count = [tempArray count];
555     NSString * searchString = [components objectAtIndex:4];
556     if ([searchString isEqualToString:@"none"] || [searchString isEqualToString:@"0"]) {
557         [_customize_vid_codec_pop selectItemAtIndex:-1];
558     } else {
559         for (NSUInteger x = 0; x < count; x++) {
560             if ([[tempArray objectAtIndex:x] isEqualToString: searchString]) {
561                 [_customize_vid_codec_pop selectItemAtIndex:x];
562                 break;
563             }
564         }
565     }
566
567     tempArray = [_audioCodecs objectAtIndex:1];
568     count = [tempArray count];
569     searchString = [components objectAtIndex:10];
570     if ([searchString isEqualToString:@"none"] || [searchString isEqualToString:@"0"]) {
571         [_customize_aud_codec_pop selectItemAtIndex:-1];
572     } else {
573         for (NSUInteger x = 0; x < count; x++) {
574             if ([[tempArray objectAtIndex:x] isEqualToString: searchString]) {
575                 [_customize_aud_codec_pop selectItemAtIndex:x];
576                 break;
577             }
578         }
579     }
580
581     tempArray = [_subsCodecs objectAtIndex:1];
582     count = [tempArray count];
583     searchString = [components objectAtIndex:14];
584     if ([searchString isEqualToString:@"none"] || [searchString isEqualToString:@"0"]) {
585         [_customize_subs_pop selectItemAtIndex:-1];
586     } else {
587         for (NSUInteger x = 0; x < count; x++) {
588             if ([[tempArray objectAtIndex:x] isEqualToString: searchString]) {
589                 [_customize_subs_pop selectItemAtIndex:x];
590                 break;
591             }
592         }
593     }
594
595     if (_currentProfile)
596         [_currentProfile release];
597     _currentProfile = [[NSMutableArray alloc] initWithArray: [profileString componentsSeparatedByString:@";"]];
598 }
599
600 - (void)selectCellByEncapsulationFormat:(NSString *)format
601 {
602     if ([format isEqualToString:@"ts"])
603         [_customize_encap_matrix selectCellWithTag:MPEGTS];
604     else if([format isEqualToString:@"webm"])
605         [_customize_encap_matrix selectCellWithTag:WEBM];
606     else if([format isEqualToString:@"ogg"])
607         [_customize_encap_matrix selectCellWithTag:OGG];
608     else if([format isEqualToString:@"ogm"])
609         [_customize_encap_matrix selectCellWithTag:OGG];
610     else if([format isEqualToString:@"mp4"])
611         [_customize_encap_matrix selectCellWithTag:MP4];
612     else if([format isEqualToString:@"mov"])
613         [_customize_encap_matrix selectCellWithTag:MP4];
614     else if([format isEqualToString:@"ps"])
615         [_customize_encap_matrix selectCellWithTag:MPEGPS];
616     else if([format isEqualToString:@"mpjpeg"])
617         [_customize_encap_matrix selectCellWithTag:MJPEG];
618     else if([format isEqualToString:@"wav"])
619         [_customize_encap_matrix selectCellWithTag:WAV];
620     else if([format isEqualToString:@"flv"])
621         [_customize_encap_matrix selectCellWithTag:FLV];
622     else if([format isEqualToString:@"mpeg1"])
623         [_customize_encap_matrix selectCellWithTag:MPEG1];
624     else if([format isEqualToString:@"mkv"])
625         [_customize_encap_matrix selectCellWithTag:MKV];
626     else if([format isEqualToString:@"raw"])
627         [_customize_encap_matrix selectCellWithTag:RAW];
628     else if([format isEqualToString:@"avi"])
629         [_customize_encap_matrix selectCellWithTag:AVI];
630     else if([format isEqualToString:@"asf"])
631         [_customize_encap_matrix selectCellWithTag:ASF];
632     else if([format isEqualToString:@"wmv"])
633         [_customize_encap_matrix selectCellWithTag:ASF];
634     else
635         msg_Err(VLCIntf, "CAS: unknown encap format requested for customization");
636 }
637
638 - (NSString *)currentEncapsulationFormatAsFileExtension:(BOOL)b_extension
639 {
640     NSUInteger cellTag = [[_customize_encap_matrix selectedCell] tag];
641     NSString * returnValue;
642     switch (cellTag) {
643         case MPEGTS:
644             returnValue = @"ts";
645             break;
646         case WEBM:
647             returnValue = @"webm";
648             break;
649         case OGG:
650             returnValue = @"ogg";
651             break;
652         case MP4:
653         {
654             if (b_extension)
655                 returnValue = @"m4v";
656             else
657                 returnValue = @"mp4";
658             break;
659         }
660         case MPEGPS:
661         {
662             if (b_extension)
663                 returnValue = @"mpg";
664             else
665                 returnValue = @"ps";
666             break;
667         }
668         case MJPEG:
669             returnValue = @"mjpeg";
670             break;
671         case WAV:
672             returnValue = @"wav";
673             break;
674         case FLV:
675             returnValue = @"flv";
676             break;
677         case MPEG1:
678         {
679             if (b_extension)
680                 returnValue = @"mpg";
681             else
682                 returnValue = @"mpeg1";
683             break;
684         }
685         case MKV:
686             returnValue = @"mkv";
687             break;
688         case RAW:
689             returnValue = @"raw";
690             break;
691         case AVI:
692             returnValue = @"avi";
693             break;
694         case ASF:
695             returnValue = @"asf";
696             break;
697
698         default:
699             returnValue = @"none";
700             break;
701     }
702
703     return returnValue;
704 }
705
706 - (NSString *)composedOptions
707 {
708     NSMutableString *composedOptions = [[NSMutableString alloc] initWithString:@":sout=#transcode{"];
709     if ([[_currentProfile objectAtIndex:1] intValue]) {
710         // video is enabled
711         [composedOptions appendFormat:@"vcodec=%@", [_currentProfile objectAtIndex:4]];
712         if (![[_currentProfile objectAtIndex:4] isEqualToString:@"none"]) {
713             if ([[_currentProfile objectAtIndex:5] intValue] > 0) // bitrate
714                 [composedOptions appendFormat:@",vb=%@", [_currentProfile objectAtIndex:5]];
715             if ([[_currentProfile objectAtIndex:6] floatValue] > 0.) // scale
716                 [composedOptions appendFormat:@",scale=%@", [_currentProfile objectAtIndex:6]];
717             if ([[_currentProfile objectAtIndex:7] floatValue] > 0.) // fps
718                 [composedOptions appendFormat:@",fps=%@", [_currentProfile objectAtIndex:7]];
719             if ([[_currentProfile objectAtIndex:8] intValue] > 0) // width
720                 [composedOptions appendFormat:@",width=%@", [_currentProfile objectAtIndex:8]];
721             if ([[_currentProfile objectAtIndex:9] intValue] > 0) // height
722                 [composedOptions appendFormat:@",height=%@", [_currentProfile objectAtIndex:9]];
723         }
724     }
725     if ([[_currentProfile objectAtIndex:2] intValue]) {
726         // audio is enabled
727
728         // add another comma in case video is enabled
729         if ([[_currentProfile objectAtIndex:1] intValue])
730             [composedOptions appendString:@","];
731
732         [composedOptions appendFormat:@"acodec=%@", [_currentProfile objectAtIndex:10]];
733         if (![[_currentProfile objectAtIndex:10] isEqualToString:@"none"]) {
734             [composedOptions appendFormat:@",ab=%@", [_currentProfile objectAtIndex:11]]; // bitrate
735             [composedOptions appendFormat:@",channels=%@", [_currentProfile objectAtIndex:12]]; // channel number
736             [composedOptions appendFormat:@",samplerate=%@", [_currentProfile objectAtIndex:13]]; // sample rate
737         }
738     }
739     if ([_currentProfile objectAtIndex:3]) {
740         // subtitles enabled
741         [composedOptions appendFormat:@",scodec=%@", [_currentProfile objectAtIndex:14]];
742         if ([[_currentProfile objectAtIndex:15] intValue])
743             [composedOptions appendFormat:@",soverlay"];
744     }
745
746     // add muxer
747     [composedOptions appendFormat:@"}:standard{mux=%@", [_currentProfile objectAtIndex:0]];
748
749     // add output destination (file only at this point)
750     [composedOptions appendFormat:@",dst=%@,access=file}", _outputDestination];
751
752     NSString * returnString = [NSString stringWithString:composedOptions];
753     [composedOptions release];
754
755     return returnString;
756 }
757
758 @end
759
760 # pragma mark -
761 # pragma mark Drag and drop handling
762
763 @implementation VLCDropEnabledBox
764
765 - (void)awakeFromNib
766 {
767     [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
768 }
769
770 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
771 {
772     b_activeDragAndDrop = YES;
773     [self setNeedsDisplay:YES];
774
775     if (OSX_SNOW_LEOPARD || OSX_LION)
776         [[NSCursor dragCopyCursor] set];
777
778     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
779         return NSDragOperationGeneric;
780
781     return NSDragOperationNone;
782 }
783
784 - (void)draggingEnded:(id < NSDraggingInfo >)sender
785 {
786     [[NSCursor arrowCursor] set];
787     b_activeDragAndDrop = NO;
788     [self setNeedsDisplay:YES];
789 }
790
791 - (void)draggingExited:(id < NSDraggingInfo >)sender
792 {
793     [[NSCursor arrowCursor] set];
794     b_activeDragAndDrop = NO;
795     [self setNeedsDisplay:YES];
796 }
797
798 - (void)drawRect:(NSRect)dirtyRect
799 {
800     if (b_activeDragAndDrop) {
801         [[NSColor colorWithCalibratedRed:(.154/.255) green:(.154/.255) blue:(.154/.255) alpha:1.] setFill];
802         NSRect frameRect = [[self contentView] bounds];
803         frameRect.origin.x += 10;
804         frameRect.origin.y += 10;
805         frameRect.size.width -= 17;
806         frameRect.size.height -= 17;
807         NSFrameRectWithWidthUsingOperation(frameRect, 4., NSCompositeHighlight);
808     }
809
810     [super drawRect:dirtyRect];
811 }
812
813 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
814 {
815     return YES;
816 }
817
818 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
819 {
820     return [[VLCConvertAndSave sharedInstance] performDragOperation: sender];
821 }
822
823 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
824 {
825     [self setNeedsDisplay:YES];
826 }
827
828 @end
829
830 @implementation VLCDropEnabledImageView
831
832 - (void)awakeFromNib
833 {
834     [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
835 }
836
837 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
838 {
839     return [[[self superview] superview] draggingEntered:sender];
840 }
841
842 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
843 {
844     return YES;
845 }
846
847 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
848 {
849     return [[VLCConvertAndSave sharedInstance] performDragOperation: sender];
850 }
851
852 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
853 {
854     [self setNeedsDisplay:YES];
855 }
856
857 @end
858
859 @implementation VLCDropEnabledButton
860
861 - (void)awakeFromNib
862 {
863     [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
864 }
865
866 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
867 {
868     return [[[self superview] superview] draggingEntered:sender];
869 }
870
871 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
872 {
873     return YES;
874 }
875
876 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
877 {
878     return [[VLCConvertAndSave sharedInstance] performDragOperation: sender];
879 }
880
881 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
882 {
883     [self setNeedsDisplay:YES];
884 }
885
886 @end