You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1316 lines
53 KiB

/*****************************************************************************
* VLCOpenWindowController.m: Open dialogues for VLC's MacOS X port
*****************************************************************************
* Copyright (C) 2002-2019 VLC authors and VideoLAN
*
* Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
* Christophe Massiot <massiot@via.ecp.fr>
* Derk-Jan Hartman <thedj@users.sourceforge.net>
* Benjamin Pracht <bigben at videolan dot org>
* Felix Paul Kühne <fkuehne at videolan dot 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 "VLCOpenWindowController.h"
#import <Cocoa/Cocoa.h>
#import <AVFoundation/AVFoundation.h>
#import <vlc_common.h>
#import <vlc_url.h>
#import "main/VLCMain.h"
#import "playlist/VLCPlaylistController.h"
#import "windows/convertandsave/VLCOutput.h"
#import "windows/VLCOpenInputMetadata.h"
NSString *const VLCOpenTextFieldWasClicked = @"VLCOpenTextFieldWasClicked";
@interface VLCOpenTextField : NSTextField
- (void)mouseDown:(NSEvent *)theEvent;
@end
@implementation VLCOpenTextField
- (void)mouseDown:(NSEvent *)theEvent
{
[[NSNotificationCenter defaultCenter] postNotificationName: VLCOpenTextFieldWasClicked
object: self];
[super mouseDown: theEvent];
}
@end
struct display_info_t
{
CGRect rect;
CGDirectDisplayID id;
};
@interface VLCOpenWindowController()
{
VLCOutput *_output;
BOOL b_outputNibLoaded;
NSArray *_avvideoDevices;
NSArray *_avaudioDevices;
NSString *_avCurrentDeviceUID;
NSString *_avCurrentAudioDeviceUID;
BOOL b_autoplay;
BOOL b_nodvdmenus;
NSView *_currentOpticalMediaView;
NSImageView *_currentOpticalMediaIconView;
NSMutableArray *_allMediaDevices;
NSArray *_opticalDevices;
NSMutableArray *_specialMediaFolders;
NSString *_filePath;
NSString *_fileSlavePath;
NSString *_subPath;
NSString *_MRL;
NSMutableArray *_displayInfos;
}
@property (readwrite, assign) NSString *MRL;
@end
@implementation VLCOpenWindowController
static NSString *kFileTabViewId = @"file";
static NSString *kDiscTabViewId = @"disc";
static NSString *kNetworkTabViewId = @"network";
static NSString *kCaptureTabViewId = @"capture";
#pragma mark -
#pragma mark Init
- (id)init
{
self = [super initWithWindowNibName:@"Open"];
return self;
}
- (void)dealloc
{
NSUInteger displayInfoCount = [_displayInfos count];
for (int i = 0; i < displayInfoCount; i ++) {
NSValue *v = [_displayInfos objectAtIndex:i];
free([v pointerValue]);
}
}
- (void)windowDidLoad
{
_output = [VLCOutput new];
[self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
[self.window setTitle: _NS("Open Source")];
[_mrlButtonLabel setTitle: _NS("Media Resource Locator (MRL)")];
[_okButton setTitle: _NS("Open")];
[_cancelButton setTitle: _NS("Cancel")];
[_outputCheckbox setTitle:_NS("Stream output:")];
[_outputSettingsButton setTitle:_NS("Settings...")];
_tabView.accessibilityLabel = _NS("Choose media input type");
[[_tabView tabViewItemAtIndex: 0] setLabel: _NS("File")];
[[_tabView tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
[[_tabView tabViewItemAtIndex: 2] setLabel: _NS("Network")];
[[_tabView tabViewItemAtIndex: 3] setLabel: _NS("Capture")];
[_fileNameLabel setStringValue: @""];
[_fileNameStubLabel setStringValue: _NS("Choose a file")];
[_fileIconWell setImage: [NSImage imageNamed:@"generic"]];
[_fileBrowseButton setTitle: _NS("Browse...")];
_fileBrowseButton.accessibilityLabel = _NS("Select a file for playback");
[_fileTreatAsPipeButton setTitle: _NS("Treat as a pipe rather than as a file")];
[_fileTreatAsPipeButton setHidden: NO];
[_fileSlaveCheckbox setTitle: _NS("Play another media synchronously")];
[_fileSelectSlaveButton setTitle: _NS("Choose...")];
_fileBrowseButton.accessibilityLabel = _NS("Select another file to play in sync with the previously selected file");
[_fileSlaveFilenameLabel setStringValue: @""];
[_fileSlaveIconWell setImage: NULL];
[_fileSubtitlesFilenameLabel setStringValue: @""];
[_fileSubtitlesIconWell setImage: NULL];
[_fileCustomTimingCheckbox setTitle: _NS("Custom playback")];
[_fileStartTimeLabel setStringValue: _NS("Start time")];
[_fileStartTimeTextField setStringValue: @""];
[_fileStopTimeLabel setStringValue: _NS("Stop time")];
[_fileStopTimeTextField setStringValue: @""];
[_discSelectorPopup removeAllItems];
[_discSelectorPopup setHidden: NO];
NSString *oVideoTS = _NS("Open VIDEO_TS / BDMV folder");
[_discNoDiscLabel setStringValue: _NS("Insert Disc")];
[_discNoDiscVideoTSButton setTitle: oVideoTS];
[_discAudioCDLabel setStringValue: _NS("Audio CD")];
[_discAudioCDTrackCountLabel setStringValue: @""];
[_discAudioCDVideoTSButton setTitle: oVideoTS];
[_discDVDLabel setStringValue: @""];
[_discDVDDisableMenusButton setTitle: _NS("Disable DVD menus")];
[_discDVDVideoTSButton setTitle: oVideoTS];
[_discDVDwomenusLabel setStringValue: @""];
[_discDVDwomenusEnableMenusButton setTitle: _NS("Enable DVD menus")];
[_discDVDwomenusVideoTSButton setTitle: oVideoTS];
[_discDVDwomenusTitleLabel setStringValue: _NS("Title")];
[_discDVDwomenusChapterLabel setStringValue: _NS("Chapter")];
[_discVCDTitleLabel setStringValue: _NS("Title")];
[_discVCDChapterLabel setStringValue: _NS("Chapter")];
[_discVCDVideoTSButton setTitle: oVideoTS];
[_discBDVideoTSButton setTitle: oVideoTS];
[_netUDPPortLabel setStringValue: _NS("Port")];
[_netUDPMAddressLabel setStringValue: _NS("IP Address")];
[_netUDPMPortLabel setStringValue: _NS("Port")];
[_netHTTPURLLabel setStringValue: _NS("URL")];
[_netHelpLabel setStringValue: _NS("To Open a usual network stream (HTTP, RTSP, RTMP, MMS, FTP, etc.), just enter the URL in the field above. If you want to open a RTP or UDP stream, press the button below.")];
[_netHelpUDPLabel setStringValue: _NS("If you want to open a multicast stream, enter the respective IP address given by the stream provider. In unicast mode, VLC will use your machine's IP automatically.\n\nTo open a stream using a different protocol, just press Cancel to close this sheet.")];
_netHTTPURLTextField.accessibilityLabel = _NS("Enter a stream URL here. To open RTP or UDP streams, use the respective button below.");
[_netUDPCancelButton setTitle: _NS("Cancel")];
[_netUDPOKButton setTitle: _NS("Open")];
[_netOpenUDPButton setTitle: _NS("Open RTP/UDP Stream")];
[_netUDPModeLabel setStringValue: _NS("Mode")];
[_netUDPProtocolLabel setStringValue: _NS("Protocol")];
[_netUDPAddressLabel setStringValue: _NS("Address")];
[[_netModeMatrix cellAtRow:0 column:0] setTitle: _NS("Unicast")];
[[_netModeMatrix cellAtRow:1 column:0] setTitle: _NS("Multicast")];
[_netUDPPortTextField setIntegerValue: config_GetInt("server-port")];
[_netUDPPortStepper setIntegerValue: config_GetInt("server-port")];
[_captureModePopup removeAllItems];
[_captureModePopup addItemWithTitle: _NS("Input Devices")];
[_captureModePopup addItemWithTitle: _NS("Screen")];
[_screenFPSLabel setStringValue: [NSString stringWithFormat:@"%@:",_NS("Frames per Second")]];
[_screenLabel setStringValue: [NSString stringWithFormat:@"%@:",_NS("Screen")]];
[_screenLeftLabel setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen left")]];
[_screenTopLabel setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen top")]];
[_screenWidthLabel setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen Width")]];
[_screenHeightLabel setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen Height")]];
[_screenFollowMouseCheckbox setTitle: _NS("Follow the mouse")];
[_screenqtkAudioCheckbox setTitle: _NS("Capture Audio")];
// setup start / stop time fields
[_fileStartTimeTextField setFormatter:[[PositionFormatter alloc] init]];
[_fileStopTimeTextField setFormatter:[[PositionFormatter alloc] init]];
// Auto collapse MRL field
self.mrlViewHeightConstraint.constant = 0;
[self updateVideoDevices];
[_qtkVideoDevicePopup removeAllItems];
msg_Dbg(getIntf(), "Found %lu video capture devices", _avvideoDevices.count);
if (_avvideoDevices.count >= 1) {
if (!_avCurrentDeviceUID)
_avCurrentDeviceUID = [[[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] uniqueID]
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSUInteger deviceCount = _avvideoDevices.count;
for (int ivideo = 0; ivideo < deviceCount; ivideo++) {
AVCaptureDevice *avDevice = [_avvideoDevices objectAtIndex:ivideo];
// allow same name for multiple times
[[_qtkVideoDevicePopup menu] addItemWithTitle:[avDevice localizedName] action:nil keyEquivalent:@""];
if ([[[avDevice uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:_avCurrentDeviceUID])
[_qtkVideoDevicePopup selectItemAtIndex:ivideo];
}
} else {
[_qtkVideoDevicePopup addItemWithTitle: _NS("None")];
}
[_qtkAudioDevicePopup removeAllItems];
[_screenqtkAudioPopup removeAllItems];
[self updateAudioDevices];
msg_Dbg(getIntf(), "Found %lu audio capture devices", _avaudioDevices.count);
if (_avaudioDevices.count >= 1) {
if (!_avCurrentAudioDeviceUID)
_avCurrentAudioDeviceUID = [[[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] uniqueID]
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSUInteger deviceCount = _avaudioDevices.count;
for (int iaudio = 0; iaudio < deviceCount; iaudio++) {
AVCaptureDevice *avAudioDevice = [_avaudioDevices objectAtIndex:iaudio];
// allow same name for multiple times
NSString *localizedName = [avAudioDevice localizedName];
[[_qtkAudioDevicePopup menu] addItemWithTitle:localizedName action:nil keyEquivalent:@""];
[[_screenqtkAudioPopup menu] addItemWithTitle:localizedName action:nil keyEquivalent:@""];
if ([[[avAudioDevice uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:_avCurrentAudioDeviceUID]) {
[_qtkAudioDevicePopup selectItemAtIndex:iaudio];
[_screenqtkAudioPopup selectItemAtIndex:iaudio];
}
}
} else {
[_qtkAudioDevicePopup addItemWithTitle: _NS("None")];
[_screenqtkAudioPopup addItemWithTitle: _NS("None")];
}
[self setSubPanel];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver: self
selector: @selector(openNetInfoChanged:)
name: NSControlTextDidChangeNotification
object: _netUDPPortTextField];
[notificationCenter addObserver: self
selector: @selector(openNetInfoChanged:)
name: NSControlTextDidChangeNotification
object: _netUDPMAddressTextField];
[notificationCenter addObserver: self
selector: @selector(openNetInfoChanged:)
name: NSControlTextDidChangeNotification
object: _netUDPMPortTextField];
[notificationCenter addObserver: self
selector: @selector(openNetInfoChanged:)
name: NSControlTextDidChangeNotification
object: _netHTTPURLTextField];
[notificationCenter addObserver: self
selector: @selector(screenFPSfieldChanged:)
name: NSControlTextDidChangeNotification
object: _screenFPSTextField];
/* register clicks on text fields */
[notificationCenter addObserver: self
selector: @selector(textFieldWasClicked:)
name: VLCOpenTextFieldWasClicked
object: nil];
/* we want to be notified about removed or added media */
_allMediaDevices = [[NSMutableArray alloc] init];
_specialMediaFolders = [[NSMutableArray alloc] init];
_displayInfos = [[NSMutableArray alloc] init];
NSNotificationCenter *sharedNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
[sharedNotificationCenter addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidMountNotification object:nil];
[sharedNotificationCenter addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidUnmountNotification object:nil];
[self qtkToggleUIElements:nil];
[self updateMediaSelector:nil];
[self scanOpticalMedia:nil];
[self setMRL: @""];
}
- (void)setMRL:(NSString *)newMRL
{
if (!newMRL)
newMRL = @"";
_MRL = newMRL;
[self.mrlTextField performSelectorOnMainThread:@selector(setStringValue:) withObject:_MRL waitUntilDone:NO];
if ([_MRL length] > 0)
[_okButton setEnabled: YES];
else
[_okButton setEnabled: NO];
}
- (NSString *)MRL
{
return _MRL;
}
- (void)setSubPanel
{
int i_index;
module_config_t * p_item;
[_fileSubCheckbox setTitle: _NS("Add Subtitle File:")];
[_fileSubPathLabel setStringValue: _NS("Choose a file")];
[_fileSubPathLabel setHidden: NO];
[_fileSubPathTextField setStringValue: @""];
[_fileSubSettingsButton setTitle: _NS("Choose...")];
_fileSubSettingsButton.accessibilityLabel = _NS("Setup subtitle playback details");
_fileBrowseButton.accessibilityLabel = _NS("Select a file for playback");
[_fileSubBrowseButton setTitle: _NS("Browse...")];
_fileSubBrowseButton.accessibilityLabel = _NS("Select a subtitle file");
[_fileSubOverrideCheckbox setTitle: _NS("Override parameters")];
[_fileSubDelayLabel setStringValue: _NS("Delay")];
[_fileSubDelayStepper setEnabled: NO];
[_fileSubFPSLabel setStringValue: _NS("FPS")];
[_fileSubFPSStepper setEnabled: NO];
[_fileSubEncodingLabel setStringValue: _NS("Subtitle encoding")];
[_fileSubEncodingPopup removeAllItems];
[_fileSubSizeLabel setStringValue: _NS("Font size")];
[_fileSubSizePopup removeAllItems];
[_fileSubAlignLabel setStringValue: _NS("Subtitle alignment")];
[_fileSubAlignPopup removeAllItems];
[_fileSubOKButton setStringValue: _NS("OK")];
_fileSubOKButton.accessibilityLabel = _NS("Dismiss the subtitle setup dialog");
[_fileSubFontBox setTitle: _NS("Font Properties")];
[_fileSubFileBox setTitle: _NS("Subtitle File")];
[[_fileSubDelayTextField formatter] setFormat:[NSString stringWithFormat:@"#,##0.000 %@", _NS("s")]];
[[_fileSubFPSTextField formatter] setFormat:[NSString stringWithFormat:@"#,##0.000 %@", _NS("fps")]];
self.fileSubFps = 1.0;
p_item = config_FindConfig("subsdec-encoding");
if (p_item) {
for (int i = 0; i < p_item->list_count; i++) {
[_fileSubEncodingPopup addItemWithTitle: _NS(p_item->list_text[i])];
[[_fileSubEncodingPopup lastItem] setRepresentedObject:[NSString stringWithFormat:@"%s", p_item->list.psz[i]]];
if (p_item->value.psz && !strcmp(p_item->value.psz, p_item->list.psz[i]))
[_fileSubEncodingPopup selectItem: [_fileSubEncodingPopup lastItem]];
}
if ([_fileSubEncodingPopup indexOfSelectedItem] < 0)
[_fileSubEncodingPopup selectItemAtIndex:0];
}
p_item = config_FindConfig("subsdec-align");
if (p_item) {
for (i_index = 0; i_index < p_item->list_count; i_index++)
[_fileSubAlignPopup addItemWithTitle: _NS(p_item->list_text[i_index])];
[_fileSubAlignPopup selectItemAtIndex: p_item->value.i];
}
p_item = config_FindConfig("freetype-rel-fontsize");
if (p_item) {
for (i_index = 0; i_index < p_item->list_count; i_index++) {
[_fileSubSizePopup addItemWithTitle: _NS(p_item->list_text[i_index])];
if (p_item->value.i == p_item->list.i[i_index])
[_fileSubSizePopup selectItemAtIndex: i_index];
}
}
}
- (void)openTarget:(NSString *)identifier
{
/* check whether we already run a modal dialog */
if ([NSApp modalWindow] != nil)
return;
// load window
[self window];
[_tabView selectTabViewItemWithIdentifier:identifier];
[_fileSubCheckbox setState: NSOffState];
NSModalResponse i_result = [NSApp runModalForWindow: self.window];
[self.window close];
// Check if dialog was canceled or stopped (NSModalResponseStop)
if (i_result <= 0)
return;
NSMutableArray *options = [NSMutableArray array];
VLCOpenInputMetadata *inputMetadata = [[VLCOpenInputMetadata alloc] init];
inputMetadata.MRLString = [self MRL];
if ([_fileSubCheckbox state] == NSOnState) {
module_config_t * p_item;
[options addObject: [NSString stringWithFormat: @"sub-file=%@", _subPath]];
if ([_fileSubOverrideCheckbox state] == NSOnState) {
[options addObject: [NSString stringWithFormat: @"sub-delay=%f", ([self fileSubDelay] * 10)]];
[options addObject: [NSString stringWithFormat: @"sub-fps=%f", [self fileSubFps]]];
}
[options addObject: [NSString stringWithFormat:
@"subsdec-encoding=%@", [[_fileSubEncodingPopup selectedItem] representedObject]]];
[options addObject: [NSString stringWithFormat:
@"subsdec-align=%li", [_fileSubAlignPopup indexOfSelectedItem]]];
p_item = config_FindConfig("freetype-rel-fontsize");
if (p_item) {
[options addObject: [NSString stringWithFormat:
@"freetype-rel-fontsize=%i",
p_item->list.i[[_fileSubSizePopup indexOfSelectedItem]]]];
}
}
if ([_fileCustomTimingCheckbox state] == NSOnState) {
NSArray *components = [[_fileStartTimeTextField stringValue] componentsSeparatedByString:@":"];
NSUInteger componentCount = [components count];
NSInteger tempValue = 0;
if (componentCount == 1)
tempValue = [[components firstObject] intValue];
else if (componentCount == 2)
tempValue = [[components firstObject] intValue] * 60 + [[components objectAtIndex:1] intValue];
else if (componentCount == 3)
tempValue = [[components firstObject] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue];
if (tempValue > 0)
[options addObject: [NSString stringWithFormat:@"start-time=%li", tempValue]];
components = [[_fileStopTimeTextField stringValue] componentsSeparatedByString:@":"];
componentCount = [components count];
if (componentCount == 1)
tempValue = [[components firstObject] intValue];
else if (componentCount == 2)
tempValue = [[components firstObject] intValue] * 60 + [[components objectAtIndex:1] intValue];
else if (componentCount == 3)
tempValue = [[components firstObject] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue];
if (tempValue != 0)
[options addObject: [NSString stringWithFormat:@"stop-time=%li", tempValue]];
}
if ([_outputCheckbox state] == NSOnState) {
NSArray *soutMRL = [_output soutMRL];
NSUInteger count = [soutMRL count];
for (NSUInteger i = 0 ; i < count ; i++)
[options addObject: [NSString stringWithString: [soutMRL objectAtIndex:i]]];
}
if ([_fileSlaveCheckbox state] && _fileSlavePath)
[options addObject: [NSString stringWithFormat: @"input-slave=%@", _fileSlavePath]];
if ([[[_tabView selectedTabViewItem] identifier] isEqualToString: kCaptureTabViewId]) {
if ([[[_captureModePopup selectedItem] title] isEqualToString: _NS("Screen")]) {
NSInteger selected_index = [_screenPopup indexOfSelectedItem];
NSValue *v = [_displayInfos objectAtIndex:selected_index];
struct display_info_t *item = (struct display_info_t *)[v pointerValue];
[options addObject: [NSString stringWithFormat: @"screen-fps=%f", [_screenFPSTextField floatValue]]];
[options addObject: [NSString stringWithFormat: @"screen-display-id=%i", item->id]];
[options addObject: [NSString stringWithFormat: @"screen-left=%i", [_screenLeftTextField intValue]]];
[options addObject: [NSString stringWithFormat: @"screen-top=%i", [_screenTopTextField intValue]]];
[options addObject: [NSString stringWithFormat: @"screen-width=%i", [_screenWidthTextField intValue]]];
[options addObject: [NSString stringWithFormat: @"screen-height=%i", [_screenHeightTextField intValue]]];
if ([_screenFollowMouseCheckbox intValue] == YES)
[options addObject: @"screen-follow-mouse"];
else
[options addObject: @"no-screen-follow-mouse"];
if ([_screenqtkAudioCheckbox state] && _avCurrentAudioDeviceUID)
[options addObject: [NSString stringWithFormat: @"input-slave=avaudiocapture://%@", _avCurrentAudioDeviceUID]];
}
else if ([[[_captureModePopup selectedItem] title] isEqualToString: _NS("Input Devices")]) {
if ([_qtkVideoCheckbox state]) {
if ([_qtkAudioCheckbox state] && _avCurrentAudioDeviceUID)
[options addObject: [NSString stringWithFormat: @"input-slave=avaudiocapture://%@", _avCurrentAudioDeviceUID]];
}
}
}
/* apply the options to our item(s) */
inputMetadata.playbackOptions = options;
[[[VLCMain sharedInstance] playlistController] addPlaylistItems:@[inputMetadata]];
}
#pragma mark -
#pragma mark Main Actions
- (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)tabViewItem
{
NSString *identifier = [tabViewItem identifier];
if ([identifier isEqualToString: kFileTabViewId])
[self openFilePathChanged: nil];
else if ([identifier isEqualToString: kDiscTabViewId])
[self scanOpticalMedia: nil];
else if ([identifier isEqualToString: kNetworkTabViewId]) {
[self openNetInfoChanged: nil];
[_netHTTPURLTextField selectText:nil];
} else if ([identifier isEqualToString: kCaptureTabViewId])
[self openCaptureModeChanged: nil];
}
- (IBAction)expandMRLfieldAction:(id)sender
{
if ([_mrlButton state] == NSOffState) {
self.mrlViewHeightConstraint.animator.constant = 0;
} else {
self.mrlViewHeightConstraint.animator.constant = 39;
}
}
- (void)openFileGeneric
{
[self openFilePathChanged: nil];
[self openTarget: kFileTabViewId];
}
- (void)openDisc
{
@synchronized (self) {
[_specialMediaFolders removeAllObjects];
}
[self scanOpticalMedia: nil];
[self openTarget: kDiscTabViewId];
}
- (void)openNet
{
[self openNetInfoChanged: nil];
[self openTarget: kNetworkTabViewId];
[_netHTTPURLTextField selectText:nil];
}
- (void)openCapture
{
[self openCaptureModeChanged: nil];
[self openTarget: kCaptureTabViewId];
}
- (void)openFileWithAction:(void (^)(NSArray *files))action;
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection: YES];
[openPanel setCanChooseDirectories: YES];
[openPanel setTitle: _NS("Open File")];
[openPanel setPrompt: _NS("Open")];
if ([openPanel runModal] == NSModalResponseOK) {
NSArray *URLs = [openPanel URLs];
NSUInteger count = [URLs count];
NSMutableArray *values = [NSMutableArray arrayWithCapacity:count];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
for (NSUInteger i = 0; i < count; i++)
[values addObject: [[URLs objectAtIndex:i] path]];
[values sortUsingSelector:@selector(caseInsensitiveCompare:)];
for (NSUInteger i = 0; i < count; i++) {
VLCOpenInputMetadata *inputMetadata;
char *psz_uri = vlc_path2uri([[values objectAtIndex:i] UTF8String], "file");
if (!psz_uri)
continue;
inputMetadata = [[VLCOpenInputMetadata alloc] init];
inputMetadata.MRLString = toNSStr(psz_uri);
free(psz_uri);
[array addObject:inputMetadata];
}
action(array);
}
}
- (IBAction)outputSettings:(id)sender
{
if (sender == self.outputCheckbox) {
self.outputSettingsButton.enabled = self.outputCheckbox.state;
return;
}
if (!b_outputNibLoaded) {
b_outputNibLoaded = [[NSBundle mainBundle] loadNibNamed:@"StreamOutput" owner:_output topLevelObjects:nil];
}
[self.window beginSheet:_output.outputSheet completionHandler:nil];
}
#pragma mark -
#pragma mark File Panel
- (void)openFilePathChanged:(NSNotification *)o_notification
{
if (_filePath && [_filePath length] > 0) {
bool b_stream = [_fileTreatAsPipeButton state];
BOOL b_dir = NO;
[[NSFileManager defaultManager] fileExistsAtPath:_filePath isDirectory:&b_dir];
char *psz_uri = vlc_path2uri([_filePath UTF8String], "file");
if (!psz_uri) return;
NSMutableString *mrlString = [NSMutableString stringWithUTF8String:psz_uri];
NSRange offile = [mrlString rangeOfString:@"file"];
free(psz_uri);
if (b_dir)
[mrlString replaceCharactersInRange:offile withString: @"directory"];
else if (b_stream)
[mrlString replaceCharactersInRange:offile withString: @"stream"];
[_fileNameLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath:_filePath]];
[_fileNameStubLabel setHidden: YES];
[_fileTreatAsPipeButton setHidden: NO];
[_fileIconWell setImage: [[NSWorkspace sharedWorkspace] iconForFile: _filePath]];
[_fileIconWell setHidden: NO];
[self setMRL: mrlString];
} else {
[_fileNameLabel setStringValue: @""];
[_fileNameStubLabel setHidden: NO];
[_fileTreatAsPipeButton setHidden: YES];
[_fileIconWell setImage: [NSImage imageNamed:@"generic"]];
[self setMRL: @""];
}
}
- (IBAction)openFileBrowse:(id)sender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection: NO];
[openPanel setCanChooseDirectories: YES];
[openPanel setTitle: _NS("Open File")];
[openPanel setPrompt: _NS("Open")];
[openPanel beginSheetModalForWindow:[sender window] completionHandler:^(NSInteger returnCode) {
if (returnCode == NSFileHandlingPanelOKButton) {
self->_filePath = [[[openPanel URLs] firstObject] path];
[self openFilePathChanged: nil];
}
}];
}
- (IBAction)openFileStreamChanged:(id)sender
{
[self openFilePathChanged: nil];
}
- (IBAction)inputSlaveAction:(id)sender
{
if (sender == _fileSlaveCheckbox)
[_fileSelectSlaveButton setEnabled: [_fileSlaveCheckbox state]];
else {
NSOpenPanel *openPanel;
openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles: YES];
[openPanel setCanChooseDirectories: NO];
if ([openPanel runModal] == NSModalResponseOK) {
_fileSlavePath = [[[openPanel URLs] firstObject] path];
}
}
if (_fileSlavePath && [_fileSlaveCheckbox state] == NSOnState) {
[_fileSlaveFilenameLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath:_fileSlavePath]];
[_fileSlaveIconWell setImage: [[NSWorkspace sharedWorkspace] iconForFile: _fileSlavePath]];
} else {
[_fileSlaveFilenameLabel setStringValue: @""];
[_fileSlaveIconWell setImage: NULL];
}
}
- (IBAction)fileTimeCustomization:(id)sender
{
BOOL b_value = [_fileCustomTimingCheckbox state];
[_fileStartTimeTextField setEnabled: b_value];
[_fileStartTimeLabel setEnabled: b_value];
[_fileStopTimeTextField setEnabled: b_value];
[_fileStopTimeLabel setEnabled: b_value];
}
#pragma mark -
#pragma mark Optical Media Panel
- (void)showOpticalMediaView:(NSView *)theView withIcon:(NSImage *)icon
{
NSRect viewRect = [theView frame];
[theView setFrame: NSMakeRect(233, 0, viewRect.size.width, viewRect.size.height)];
[theView setAutoresizesSubviews: YES];
NSView *opticalTabView = [[_tabView tabViewItemAtIndex: [_tabView indexOfTabViewItemWithIdentifier:kDiscTabViewId]] view];
if (_currentOpticalMediaView) {
[[opticalTabView animator] replaceSubview: _currentOpticalMediaView with: theView];
} else {
[[opticalTabView animator] addSubview: theView];
}
_currentOpticalMediaView = theView;
NSImageView *imageView = [[NSImageView alloc] init];
[imageView setFrame: NSMakeRect(53, 61, 128, 128)];
[icon setSize: NSMakeSize(128,128)];
[imageView setImage: icon];
if (_currentOpticalMediaIconView) {
[[opticalTabView animator] replaceSubview: _currentOpticalMediaIconView with: imageView];
} else {
[[opticalTabView animator] addSubview: imageView];
}
_currentOpticalMediaIconView = imageView;
[_currentOpticalMediaView setNeedsDisplay: YES];
[_currentOpticalMediaIconView setNeedsDisplay: YES];
[opticalTabView setNeedsDisplay: YES];
[opticalTabView displayIfNeeded];
}
- (void)showOpticalAtPath: (NSDictionary *)valueDictionary
{
NSString *diskType = [valueDictionary objectForKey:@"mediaType"];
NSString *opticalDevicePath = [valueDictionary objectForKey:@"path"];
NSString *devicePath = [valueDictionary objectForKey:@"devicePath"];
NSImage *image = [valueDictionary objectForKey:@"image"];
if ([diskType isEqualToString: kVLCMediaDVD] || [diskType isEqualToString: kVLCMediaVideoTSFolder]) {
[_discDVDLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath:opticalDevicePath]];
[_discDVDwomenusLabel setStringValue: [_discDVDLabel stringValue]];
if (!b_nodvdmenus) {
[self setMRL: [NSString stringWithFormat: @"dvdnav://%@", devicePath]];
[self showOpticalMediaView: _discDVDView withIcon:image];
} else {
[self setMRL: [NSString stringWithFormat: @"dvdread://%@#%i:%i-", devicePath, [_discDVDwomenusTitleTextField intValue], [_discDVDwomenusChapterTextField intValue]]];
[self showOpticalMediaView: _discDVDwomenusView withIcon:image];
}
} else if ([diskType isEqualToString: kVLCMediaAudioCD]) {
[_discAudioCDLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath: opticalDevicePath]];
[_discAudioCDTrackCountLabel setStringValue: [NSString stringWithFormat:_NS("%i tracks"), [[[NSFileManager defaultManager] subpathsOfDirectoryAtPath: opticalDevicePath error:NULL] count] - 1]]; // minus .TOC.plist
[self showOpticalMediaView: _discAudioCDView withIcon: image];
[self setMRL: [NSString stringWithFormat: @"cdda://%@", devicePath]];
} else if ([diskType isEqualToString: kVLCMediaVCD]) {
[_discVCDLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath: opticalDevicePath]];
[self showOpticalMediaView: _discVCDView withIcon: image];
[self setMRL: [NSString stringWithFormat: @"vcd://%@#%i:%i", devicePath, [_discVCDTitleTextField intValue], [_discVCDChapterTextField intValue]]];
} else if ([diskType isEqualToString: kVLCMediaSVCD]) {
[_discVCDLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath: opticalDevicePath]];
[self showOpticalMediaView: _discVCDView withIcon: image];
[self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", devicePath, [_discVCDTitleTextField intValue], [_discVCDChapterTextField intValue]]];
} else if ([diskType isEqualToString: kVLCMediaBD] || [diskType isEqualToString: kVLCMediaBDMVFolder]) {
[_discBDLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath: opticalDevicePath]];
[self showOpticalMediaView: _discBDView withIcon: image];
[self setMRL: [NSString stringWithFormat: @"bluray://%@", opticalDevicePath]];
} else {
msg_Warn(getIntf(), "unknown disk type, no idea what to display");
[self showOpticalMediaView: _discNoDiscView withIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
}
}
- (NSDictionary *)scanPath:(NSURL *)url
{
NSString *path = [url path];
NSString *type = getVolumeTypeFromMountPath(path);
NSImage *image = [[NSWorkspace sharedWorkspace] iconForFile: path];
NSString *devicePath;
// BDMV path must not end with BDMV directory
if([type isEqualToString: kVLCMediaBDMVFolder]) {
if([[path lastPathComponent] isEqualToString: @"BDMV"]) {
path = [path stringByDeletingLastPathComponent];
}
}
if ([type isEqualToString: kVLCMediaVideoTSFolder] ||
[type isEqualToString: kVLCMediaBD] ||
[type isEqualToString: kVLCMediaBDMVFolder] ||
[type isEqualToString: kVLCMediaUnknown])
devicePath = path;
else
devicePath = getBSDNodeFromMountPath(path);
return [NSDictionary dictionaryWithObjectsAndKeys: path, @"path",
devicePath, @"devicePath",
type, @"mediaType",
image, @"image", nil];
}
- (void)scanDevices
{
@autoreleasepool {
NSArray *mountURLs = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:@[NSURLVolumeIsRemovableKey] options:NSVolumeEnumerationSkipHiddenVolumes];
NSUInteger count = [mountURLs count];
NSMutableArray *o_result = [NSMutableArray array];
for (NSUInteger i = 0; i < count; i++) {
NSURL *currentURL = [mountURLs objectAtIndex:i];
NSNumber *isRemovable = nil;
if (![currentURL getResourceValue:&isRemovable forKey:NSURLVolumeIsRemovableKey error:nil] || !isRemovable) {
msg_Warn(getIntf(), "Cannot find removable flag for mount point");
continue;
}
if (!isRemovable.boolValue)
continue;
[o_result addObject: [self scanPath:currentURL]];
}
@synchronized (self) {
_opticalDevices = [[NSArray alloc] initWithArray: o_result];
}
[self performSelectorOnMainThread:@selector(updateMediaSelector:) withObject:nil waitUntilDone:NO];
}
}
- (void)scanSpecialPath:(NSURL *)oPath
{
@autoreleasepool {
NSDictionary *o_dict = [self scanPath:oPath];
@synchronized (self) {
[_specialMediaFolders addObject:o_dict];
}
[self performSelectorOnMainThread:@selector(updateMediaSelector:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:NO];
}
}
- (void)scanOpticalMedia:(NSNotification *)o_notification
{
[NSThread detachNewThreadSelector:@selector(scanDevices) toTarget:self withObject:nil];
}
- (void)updateMediaSelector:(NSNumber *)selection
{
[_allMediaDevices removeAllObjects];
[_discSelectorPopup removeAllItems];
@synchronized (self) {
[_allMediaDevices addObjectsFromArray:_opticalDevices];
[_allMediaDevices addObjectsFromArray:_specialMediaFolders];
}
NSUInteger count = [_allMediaDevices count];
if (count > 0) {
for (NSUInteger i = 0; i < count ; i++) {
NSDictionary *o_dict = [_allMediaDevices objectAtIndex:i];
[_discSelectorPopup addItemWithTitle: [[NSFileManager defaultManager] displayNameAtPath:[o_dict objectForKey:@"path"]]];
}
if ([_discSelectorPopup numberOfItems] <= 1)
[_discSelectorPopup setHidden: YES];
else
[_discSelectorPopup setHidden: NO];
// select newly added media folder
if (selection && [selection boolValue])
[_discSelectorPopup selectItemAtIndex: [[_discSelectorPopup itemArray] count] - 1];
// only trigger MRL update if the tab view is active
if ([[[_tabView selectedTabViewItem] identifier] isEqualToString:kDiscTabViewId])
[self discSelectorChanged:nil];
} else {
msg_Dbg(getIntf(), "no optical media found");
[_discSelectorPopup setHidden: YES];
[self setMRL:@""];
[self showOpticalMediaView: _discNoDiscView withIcon: [NSImage imageNamed: @"NSApplicationIcon"]];
}
}
- (IBAction)discSelectorChanged:(id)sender
{
[self showOpticalAtPath:[_allMediaDevices objectAtIndex:[_discSelectorPopup indexOfSelectedItem]]];
}
- (IBAction)openSpecialMediaFolder:(id)sender
{
/* this is currently for VIDEO_TS and BDMV folders */
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection: NO];
[openPanel setCanChooseDirectories: YES];
[openPanel setTitle: [sender title]];
[openPanel setPrompt: _NS("Open")];
/* work-around for Mountain Lion, which treats folders called "BDMV" including an item named "INDEX.BDM"
* as a _FILE_. Don't ask, move on. There is nothing to see here */
[openPanel setCanChooseFiles: YES];
[openPanel setAllowedFileTypes:[NSArray arrayWithObject:@"public.directory"]];
if ([openPanel runModal] == NSModalResponseOK) {
NSURL *path = openPanel.URL;
if (path) {
[NSThread detachNewThreadSelector:@selector(scanSpecialPath:) toTarget:self withObject:path];
}
}
}
- (IBAction)dvdreadOptionChanged:(id)sender
{
NSString *devicePath = [[_allMediaDevices objectAtIndex:[_discSelectorPopup indexOfSelectedItem]] objectForKey:@"devicePath"];
if (sender == _discDVDwomenusEnableMenusButton) {
b_nodvdmenus = NO;
[self setMRL: [NSString stringWithFormat: @"dvdnav://%@", devicePath]];
[self showOpticalMediaView:_discDVDView withIcon:[_currentOpticalMediaIconView image]];
return;
}
if (sender == _discDVDDisableMenusButton) {
b_nodvdmenus = YES;
[self showOpticalMediaView:_discDVDwomenusView withIcon:[_currentOpticalMediaIconView image]];
}
if (sender == _discDVDwomenusTitleTextField)
[_discDVDwomenusTitleStepper setIntValue: [_discDVDwomenusTitleTextField intValue]];
if (sender == _discDVDwomenusTitleStepper)
[_discDVDwomenusTitleTextField setIntValue: [_discDVDwomenusTitleStepper intValue]];
if (sender == _discDVDwomenusChapterTextField)
[_discDVDwomenusChapterStepper setIntValue: [_discDVDwomenusChapterTextField intValue]];
if (sender == _discDVDwomenusChapterStepper)
[_discDVDwomenusChapterTextField setIntValue: [_discDVDwomenusChapterStepper intValue]];
[self setMRL: [NSString stringWithFormat: @"dvdread://%@#%i:%i-", devicePath, [_discDVDwomenusTitleTextField intValue], [_discDVDwomenusChapterTextField intValue]]];
}
- (IBAction)vcdOptionChanged:(id)sender
{
if (sender == _discVCDTitleTextField)
[_discVCDTitleStepper setIntValue: [_discVCDTitleTextField intValue]];
if (sender == _discVCDTitleStepper)
[_discVCDTitleTextField setIntValue: [_discVCDTitleStepper intValue]];
if (sender == _discVCDChapterTextField)
[_discVCDChapterStepper setIntValue: [_discVCDChapterTextField intValue]];
if (sender == _discVCDChapterStepper)
[_discVCDChapterTextField setIntValue: [_discVCDChapterStepper intValue]];
NSString *devicePath = [[_allMediaDevices objectAtIndex:[_discSelectorPopup indexOfSelectedItem]] objectForKey:@"devicePath"];
[self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", devicePath, [_discVCDTitleTextField intValue], [_discVCDChapterTextField intValue]]];
}
#pragma mark -
#pragma mark Network Panel
- (void)textFieldWasClicked:(NSNotification *)notification
{
if ([notification object] == _netUDPPortTextField)
[_netModeMatrix selectCellAtRow: 0 column: 0];
else if ([notification object] == _netUDPMAddressTextField ||
[notification object] == _netUDPMPortTextField)
[_netModeMatrix selectCellAtRow: 1 column: 0];
else
[_netModeMatrix selectCellAtRow: 2 column: 0];
[self openNetInfoChanged: nil];
}
- (IBAction)openNetModeChanged:(id)sender
{
if (sender == _netModeMatrix) {
if ([[sender selectedCell] tag] == 0)
[self.window makeFirstResponder: _netUDPPortTextField];
else if ([[sender selectedCell] tag] == 1)
[self.window makeFirstResponder: _netUDPMAddressTextField];
else
msg_Warn(getIntf(), "Unknown sender tried to change UDP/RTP mode");
}
[self openNetInfoChanged: nil];
}
- (IBAction)openNetStepperChanged:(id)sender
{
NSInteger i_tag = [sender tag];
if (i_tag == 0) {
[_netUDPPortTextField setIntValue: [_netUDPPortStepper intValue]];
[[NSNotificationCenter defaultCenter] postNotificationName: VLCOpenTextFieldWasClicked
object: _netUDPPortTextField];
[self.window makeFirstResponder: _netUDPPortTextField];
}
else if (i_tag == 1) {
[_netUDPMPortTextField setIntValue: [_netUDPMPortStepper intValue]];
[[NSNotificationCenter defaultCenter] postNotificationName: VLCOpenTextFieldWasClicked
object: _netUDPMPortTextField];
[self.window makeFirstResponder: _netUDPMPortTextField];
}
[self openNetInfoChanged: nil];
}
- (void)openNetInfoChanged:(NSNotification *)o_notification
{
NSString *mrlString;
if ([_netUDPPanel isVisible]) {
NSString *mode;
mode = [[_netModeMatrix selectedCell] title];
if ([mode isEqualToString: _NS("Unicast")]) {
int port = [_netUDPPortTextField intValue];
if ([[_netUDPProtocolMatrix selectedCell] tag] == 0)
mrlString = @"udp://";
else
mrlString = @"rtp://";
if (port != config_GetInt("server-port")) {
mrlString =
[mrlString stringByAppendingFormat: @"@:%i", port];
}
}
else if ([mode isEqualToString: _NS("Multicast")]) {
NSString *oAddress = [_netUDPMAddressTextField stringValue];
int iPort = [_netUDPMPortTextField intValue];
if ([[_netUDPProtocolMatrix selectedCell] tag] == 0)
mrlString = [NSString stringWithFormat: @"udp://@%@", oAddress];
else
mrlString = [NSString stringWithFormat: @"rtp://@%@", oAddress];
if (iPort != config_GetInt("server-port")) {
mrlString =
[mrlString stringByAppendingFormat: @":%i", iPort];
}
}
} else
mrlString = [_netHTTPURLTextField stringValue];
[self setMRL: mrlString];
}
- (IBAction)openNetUDPButtonAction:(id)sender
{
if (sender == _netOpenUDPButton) {
[self.window beginSheet:self.netUDPPanel
completionHandler:nil];
[self openNetInfoChanged:nil];
}
else if (sender == _netUDPCancelButton) {
[_netUDPPanel orderOut: sender];
[NSApp endSheet: _netUDPPanel];
}
else if (sender == _netUDPOKButton) {
NSString *mrlString;
if ([[[_netModeMatrix selectedCell] title] isEqualToString: _NS("Unicast")]) {
int port = [_netUDPPortTextField intValue];
if ([[_netUDPProtocolMatrix selectedCell] tag] == 0)
mrlString = @"udp://";
else
mrlString = @"rtp://";
if (port != config_GetInt("server-port")) {
mrlString =
[mrlString stringByAppendingFormat: @"@:%i", port];
}
}
else if ([[[_netModeMatrix selectedCell] title] isEqualToString: _NS("Multicast")]) {
NSString *oAddress = [_netUDPMAddressTextField stringValue];
int iPort = [_netUDPMPortTextField intValue];
if ([[_netUDPProtocolMatrix selectedCell] tag] == 0)
mrlString = [NSString stringWithFormat: @"udp://@%@", oAddress];
else
mrlString = [NSString stringWithFormat: @"rtp://@%@", oAddress];
if (iPort != config_GetInt("server-port")) {
mrlString =
[mrlString stringByAppendingFormat: @":%i", iPort];
}
}
[self setMRL: mrlString];
[_netHTTPURLTextField setStringValue: mrlString];
[_netUDPPanel orderOut: sender];
[NSApp endSheet: _netUDPPanel];
}
}
#pragma mark -
#pragma mark Capture Panel
- (IBAction)openCaptureModeChanged:(id)sender
{
if ([[[_captureModePopup selectedItem] title] isEqualToString: _NS("Screen")]) {
[_captureTabView selectTabViewItemAtIndex:1];
[self setMRL: @"screen://"];
[_screenHeightTextField setIntegerValue: config_GetInt("screen-height")];
[_screenWidthTextField setIntegerValue: config_GetInt("screen-width")];
[_screenFPSTextField setFloatValue: config_GetFloat("screen-fps")];
[_screenLeftTextField setIntegerValue: config_GetInt("screen-left")];
[_screenTopTextField setIntegerValue: config_GetInt("screen-top")];
[_screenFollowMouseCheckbox setIntegerValue: config_GetInt("screen-follow-mouse")];
NSInteger screenIindex = config_GetInt("screen-index");
NSInteger displayID = config_GetInt("screen-display-id");
unsigned int displayCount = 0;
CGError returnedError;
struct display_info_t *item;
NSValue *v;
returnedError = CGGetOnlineDisplayList(0, NULL, &displayCount);
if (!returnedError) {
CGDirectDisplayID *ids;
ids = (CGDirectDisplayID *)vlc_alloc(displayCount, sizeof(CGDirectDisplayID));
returnedError = CGGetOnlineDisplayList(displayCount, ids, &displayCount);
if (!returnedError) {
NSUInteger displayInfoCount = [_displayInfos count];
for (NSUInteger i = 0; i < displayInfoCount; i ++) {
v = [_displayInfos objectAtIndex:i];
free([v pointerValue]);
}
[_displayInfos removeAllObjects];
[_screenPopup removeAllItems];
for (unsigned int i = 0; i < displayCount; i ++) {
item = (struct display_info_t *)malloc(sizeof(struct display_info_t));
item->id = ids[i];
item->rect = CGDisplayBounds(item->id);
[_screenPopup addItemWithTitle: [NSString stringWithFormat:@"Screen %d (%dx%d)", i + 1, (int)item->rect.size.width, (int)item->rect.size.height]];
v = [NSValue valueWithPointer:item];
[_displayInfos addObject:v];
if (i == 0 || displayID == item->id || screenIindex - 1 == i) {
[_screenPopup selectItemAtIndex: i];
[_screenLeftStepper setMaxValue: item->rect.size.width];
[_screenTopStepper setMaxValue: item->rect.size.height];
[_screenWidthStepper setMaxValue: item->rect.size.width];
[_screenHeightStepper setMaxValue: item->rect.size.height];
}
}
}
free(ids);
}
}
else if ([[[_captureModePopup selectedItem] title] isEqualToString: _NS("Input Devices")]) {
[_captureTabView selectTabViewItemAtIndex:0];
[self qtkChanged:nil];
[self qtkAudioChanged:nil];
[self setMRL: @""];
if ([_qtkVideoCheckbox state] && _avCurrentDeviceUID)
[self setMRL:[NSString stringWithFormat:@"avcapture://%@", _avCurrentDeviceUID]];
else if ([_qtkAudioCheckbox state] && _avCurrentAudioDeviceUID)
[self setMRL:[NSString stringWithFormat:@"avaudiocapture://%@", _avCurrentAudioDeviceUID]];
}
}
// Screen actions
- (void)screenFPSfieldChanged:(NSNotification *)o_notification
{
[_screenFPSStepper setFloatValue: [_screenFPSTextField floatValue]];
if ([[_screenFPSTextField stringValue] isEqualToString: @""])
[_screenFPSTextField setFloatValue: 1.0];
[self setMRL: @"screen://"];
}
- (IBAction)screenChanged:(id)sender
{
NSInteger selected_index = [_screenPopup indexOfSelectedItem];
if (selected_index >= [_displayInfos count])
return;
NSValue *v = [_displayInfos objectAtIndex:selected_index];
struct display_info_t *item = (struct display_info_t *)[v pointerValue];
[_screenLeftStepper setMaxValue: item->rect.size.width];
[_screenTopStepper setMaxValue: item->rect.size.height];
[_screenWidthStepper setMaxValue: item->rect.size.width];
[_screenHeightStepper setMaxValue: item->rect.size.height];
[_screenqtkAudioPopup setEnabled: [_screenqtkAudioCheckbox state]];
}
- (IBAction)screenAudioChanged:(id)sender
{
[_screenqtkAudioPopup setEnabled:_screenqtkAudioCheckbox.state];
}
// QTKit Recording actions
- (IBAction)qtkChanged:(id)sender
{
NSInteger selectedDevice = [_qtkVideoDevicePopup indexOfSelectedItem];
if (selectedDevice >= _avvideoDevices.count)
return;
_avCurrentDeviceUID = [[(AVCaptureDevice *)[_avvideoDevices objectAtIndex:selectedDevice] uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
- (IBAction)qtkAudioChanged:(id)sender
{
NSInteger selectedDevice = [_qtkAudioDevicePopup indexOfSelectedItem];
if (selectedDevice >= _avaudioDevices.count)
return;
_avCurrentAudioDeviceUID = [[(AVCaptureDevice *)[_avaudioDevices objectAtIndex:selectedDevice] uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
- (IBAction)qtkToggleUIElements:(id)sender
{
[_qtkAudioDevicePopup setEnabled:[_qtkAudioCheckbox state]];
BOOL b_state = [_qtkVideoCheckbox state];
[_qtkVideoDevicePopup setEnabled:b_state];
[self qtkAudioChanged:sender];
[self qtkChanged:sender];
[self openCaptureModeChanged:sender];
}
#pragma mark -
#pragma mark Subtitle Settings
- (IBAction)subsChanged:(id)sender
{
if ([_fileSubCheckbox state] == NSOnState) {
[_fileSubSettingsButton setEnabled:YES];
if (_subPath) {
[_fileSubtitlesFilenameLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath:_subPath]];
[_fileSubtitlesIconWell setImage: [[NSWorkspace sharedWorkspace] iconForFile:_subPath]];
}
} else {
[_fileSubSettingsButton setEnabled:NO];
[_fileSubtitlesFilenameLabel setStringValue: @""];
[_fileSubtitlesIconWell setImage: NULL];
}
}
- (IBAction)subSettings:(id)sender
{
[[self window] beginSheet:self.fileSubSheet completionHandler:nil];
}
- (IBAction)subCloseSheet:(id)sender
{
[self subsChanged: nil];
[_fileSubSheet orderOut:sender];
[NSApp endSheet: _fileSubSheet];
}
- (IBAction)subFileBrowse:(id)sender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection: NO];
[openPanel setTitle: _NS("Open File")];
[openPanel setPrompt: _NS("Open")];
if ([openPanel runModal] == NSModalResponseOK) {
_subPath = [[[openPanel URLs] firstObject] path];
[_fileSubtitlesFilenameLabel setStringValue: [[NSFileManager defaultManager] displayNameAtPath:_subPath]];
[_fileSubPathTextField setStringValue: [_fileSubtitlesFilenameLabel stringValue]];
[_fileSubPathLabel setHidden: YES];
[_fileSubtitlesIconWell setImage: [[NSWorkspace sharedWorkspace] iconForFile:_subPath]];
[_fileSubIconView setImage: [_fileSubtitlesIconWell image]];
} else {
[_fileSubPathLabel setHidden: NO];
[_fileSubPathTextField setStringValue:@""];
[_fileSubtitlesFilenameLabel setStringValue:@""];
[_fileSubtitlesIconWell setImage: nil];
[_fileSubIconView setImage: nil];
}
}
- (IBAction)subOverride:(id)sender
{
BOOL b_state = [_fileSubOverrideCheckbox state];
[_fileSubDelayTextField setEnabled: b_state];
[_fileSubDelayStepper setEnabled: b_state];
[_fileSubFPSTextField setEnabled: b_state];
[_fileSubFPSStepper setEnabled: b_state];
}
#pragma mark -
#pragma mark Miscellaneous
- (IBAction)panelCancel:(id)sender
{
[NSApp stopModalWithCode: 0];
}
- (IBAction)panelOk:(id)sender
{
if ([[self MRL] length])
[NSApp stopModalWithCode: 1];
else
NSBeep();
}
- (void)updateVideoDevices
{
_avvideoDevices = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]
arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]];
}
- (void)updateAudioDevices
{
_avaudioDevices = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]
arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]];
}
@end