[vlc-commits] [Git][videolan/vlc][master] 4 commits: macosx: add dialog to manually open servers for browsing
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Wed Apr 29 12:39:02 UTC 2026
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
3de84566 by Felix Paul Kühne at 2026-04-29T13:54:06+02:00
macosx: add dialog to manually open servers for browsing
- - - - -
b9344055 by Felix Paul Kühne at 2026-04-29T13:54:06+02:00
macosx: fix refresh of media sources
Previously, there was no reload after preparse.
- - - - -
e1c09d34 by Felix Paul Kühne at 2026-04-29T13:54:06+02:00
macosx: support browsing arbitrary MRLs
- - - - -
03f30a16 by Felix Paul Kühne at 2026-04-29T13:54:06+02:00
macosx: VLCInputNodePathControlItem: add description method
- - - - -
18 changed files:
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/UI/MainMenu.xib
- modules/gui/macosx/library/VLCInputNodePathControlItem.m
- modules/gui/macosx/library/VLCLibraryWindow.h
- modules/gui/macosx/library/VLCLibraryWindow.m
- modules/gui/macosx/library/VLCLibraryWindowNavigationSidebarViewController.m
- modules/gui/macosx/library/media-source/VLCLibraryMediaSourceViewController.h
- modules/gui/macosx/library/media-source/VLCLibraryMediaSourceViewController.m
- modules/gui/macosx/library/media-source/VLCMediaSource.h
- modules/gui/macosx/library/media-source/VLCMediaSource.m
- modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.h
- modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.m
- modules/gui/macosx/library/media-source/VLCMediaSourceDataSource.m
- modules/gui/macosx/menus/VLCMainMenu.h
- modules/gui/macosx/menus/VLCMainMenu.m
- + modules/gui/macosx/windows/VLCConnectToServerDialog.h
- + modules/gui/macosx/windows/VLCConnectToServerDialog.m
- po/POTFILES.in
Changes:
=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -481,6 +481,8 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/views/VLCVolumeSliderCell.m \
gui/macosx/windows/VLCAboutWindowController.h \
gui/macosx/windows/VLCAboutWindowController.m \
+ gui/macosx/windows/VLCConnectToServerDialog.h \
+ gui/macosx/windows/VLCConnectToServerDialog.m \
gui/macosx/windows/VLCDetachedAudioWindow.h \
gui/macosx/windows/VLCDetachedAudioWindow.m \
gui/macosx/windows/VLCErrorWindowController.h \
=====================================
modules/gui/macosx/UI/MainMenu.xib
=====================================
@@ -77,6 +77,7 @@
<outlet property="next" destination="5152" id="uop-ZB-n1Q"/>
<outlet property="normal_window" destination="1170" id="T6u-xA-21q"/>
<outlet property="openSubtitleFile" destination="5515" id="XNQ-8J-eQH"/>
+ <outlet property="connect_to_server" destination="cts0" id="cts-01-out"/>
<outlet property="open_capture" destination="3292" id="wtp-L5-xZc"/>
<outlet property="open_disc" destination="413" id="ak8-1y-ze4"/>
<outlet property="open_file" destination="816" id="iH5-bK-QzN"/>
@@ -292,6 +293,11 @@
<action selector="intfOpenCapture:" target="-2" id="aLY-nh-hau"/>
</connections>
</menuItem>
+ <menuItem title="Connect to Server..." keyEquivalent="k" id="cts0">
+ <connections>
+ <action selector="intfConnectToServer:" target="-2" id="cts-02-act"/>
+ </connections>
+ </menuItem>
<menuItem isSeparatorItem="YES" id="2307">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
=====================================
modules/gui/macosx/library/VLCInputNodePathControlItem.m
=====================================
@@ -61,4 +61,13 @@
return self;
}
+- (NSString *)description
+{
+ if (_inputNode != nil && _inputNode.inputItem != nil) {
+ return [NSString stringWithFormat:@"path: %@", _inputNode.inputItem.path];
+ } else {
+ return @"no input node";
+ }
+}
+
@end
=====================================
modules/gui/macosx/library/VLCLibraryWindow.h
=====================================
@@ -143,7 +143,7 @@ extern NSString * const VLCLibraryWindowEmbeddedVideoPlaybackActiveKey;
usingConstraints:(NSArray<NSLayoutConstraint *> *)constraints
displayingMessage:(NSString *)message;
- (void)displayNoResultsMessage;
-- (void)goToLocalFolderMrl:(NSString *)mrl;
+- (void)browseFolderByMrl:(NSString *)mrl;
- (IBAction)goToBrowseSection:(id)sender;
- (IBAction)sortLibrary:(id)sender;
=====================================
modules/gui/macosx/library/VLCLibraryWindow.m
=====================================
@@ -344,10 +344,10 @@ static int ShowController(vlc_object_t * __unused p_this,
}
}
-- (void)goToLocalFolderMrl:(NSString *)mrl
+- (void)browseFolderByMrl:(NSString *)mrl
{
[self goToBrowseSection:self];
- [(VLCLibraryMediaSourceViewController *)self.librarySegmentViewController presentLocalFolderMrl:mrl];
+ [(VLCLibraryMediaSourceViewController *)self.librarySegmentViewController browseFolderByMrl:mrl];
}
- (IBAction)sortLibrary:(id)sender
=====================================
modules/gui/macosx/library/VLCLibraryWindowNavigationSidebarViewController.m
=====================================
@@ -417,7 +417,7 @@ static NSString * const VLCLibrarySegmentCellIdentifier = @"VLCLibrarySegmentCel
VLCLibrarySegmentBookmarkedLocation * const bookmarkedLocation =
(VLCLibrarySegmentBookmarkedLocation *)representedObject;
self.libraryWindow.librarySegmentType = bookmarkedLocation.segmentType;
- [self.libraryWindow goToLocalFolderMrl:bookmarkedLocation.mrl];
+ [self.libraryWindow browseFolderByMrl:bookmarkedLocation.mrl];
} else if ([representedObject isKindOfClass:VLCMediaLibraryGroup.class]) {
[self.libraryWindow presentLibraryItem:(VLCMediaLibraryGroup *)representedObject];
}
=====================================
modules/gui/macosx/library/media-source/VLCLibraryMediaSourceViewController.h
=====================================
@@ -55,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)presentBrowseView;
- (void)presentStreamsView;
-- (void)presentLocalFolderMrl:(NSString *)mrl;
+- (void)browseFolderByMrl:(NSString *)mrl;
@end
=====================================
modules/gui/macosx/library/media-source/VLCLibraryMediaSourceViewController.m
=====================================
@@ -232,10 +232,10 @@
[_baseDataSource reloadViews];
}
-- (void)presentLocalFolderMrl:(NSString *)mrl
+- (void)browseFolderByMrl:(NSString *)mrl
{
[self presentBrowseView];
- [self.baseDataSource presentLocalFolderMrl:mrl];
+ [self.baseDataSource browseFolderByMrl:mrl];
}
@end
=====================================
modules/gui/macosx/library/media-source/VLCMediaSource.h
=====================================
@@ -42,7 +42,7 @@ extern NSString *VLCMediaSourcePreparsingEnded;
andPreparser:(vlc_preparser_t *)p_preparser
forCategory:(enum services_discovery_category_e)category;
- (instancetype)initMyFoldersMediaSourceWithPreparser:(vlc_preparser_t *)p_preparser;
-- (instancetype)initWithLocalFolderMrl:(NSString *)mrl
+- (instancetype)initWithFolderMrl:(NSString *)mrl
andPreparser:(vlc_preparser_t *)p_preparser;
- (nullable NSError *)preparseInputNodeWithinTree:(VLCInputNode *)inputNode;
=====================================
modules/gui/macosx/library/media-source/VLCMediaSource.m
=====================================
@@ -97,6 +97,7 @@ static const struct vlc_media_tree_callbacks treeCallbacks = {
static const char *const localDevicesDescription = "My Machine";
static const char *const myFoldersDescription = "My Folders";
+static const char *const remoteBrowseDescription = "Remote Browse";
#pragma mark - VLCMediaSource methods
@implementation VLCMediaSource
@@ -215,20 +216,38 @@ static const char *const myFoldersDescription = "My Folders";
return self;
}
-- (instancetype)initWithLocalFolderMrl:(NSString *)mrl
- andPreparser:(vlc_preparser_t *)p_preparser
+- (instancetype)initWithFolderMrl:(NSString *)mrl
+ andPreparser:(vlc_preparser_t *)p_preparser
{
self = [super init];
if (self) {
_p_preparser = p_preparser;
+ NSURL * const directoryUrl = [NSURL URLWithString:mrl];
+ const BOOL isFileUrl = directoryUrl.isFileURL;
+
_p_mediaSource = malloc(sizeof(vlc_media_source_t));
if (!_p_mediaSource) {
return self;
}
- _p_mediaSource->description = myFoldersDescription;
- _p_mediaSource->tree = calloc(1, sizeof(vlc_media_tree_t));
+ if (isFileUrl) {
+ _category = SD_CAT_MYCOMPUTER;
+ _p_mediaSource->description = myFoldersDescription;
+ _p_mediaSource->tree = calloc(1, sizeof(vlc_media_tree_t));
+
+ BOOL mrlTargetIsDirectory = NO;
+ const BOOL mrlTargetExists = [NSFileManager.defaultManager
+ fileExistsAtPath:directoryUrl.path
+ isDirectory:&mrlTargetIsDirectory];
+ if (!mrlTargetExists || !mrlTargetIsDirectory) {
+ return nil;
+ }
+ } else {
+ _category = SD_CAT_LAN;
+ _p_mediaSource->description = remoteBrowseDescription;
+ _p_mediaSource->tree = vlc_media_tree_New();
+ }
if (_p_mediaSource->tree == NULL) {
free(_p_mediaSource);
@@ -236,26 +255,26 @@ static const char *const myFoldersDescription = "My Folders";
return self;
}
- _category = SD_CAT_MYCOMPUTER;
-
- NSFileManager * const fileManager = NSFileManager.defaultManager;
- NSURL * const directoryUrl = [NSURL URLWithString:mrl];
- BOOL mrlTargetIsDirectory = NO;
- const BOOL mrlTargetExists = [fileManager fileExistsAtPath:directoryUrl.path
- isDirectory:&mrlTargetIsDirectory];
- if (!mrlTargetExists || !mrlTargetIsDirectory) {
- return nil;
- }
-
const char * const directoryPath = mrl.UTF8String;
const char * const directoryDesc = mrl.lastPathComponent.UTF8String;
input_item_t * const directoryItem = input_item_NewExt(directoryPath,
directoryDesc,
0,
ITEM_TYPE_DIRECTORY,
- ITEM_LOCAL);
- input_item_node_t * const directoryNode = input_item_node_Create(directoryItem);
- _p_mediaSource->tree->root = *directoryNode;
+ isFileUrl ? ITEM_LOCAL : ITEM_NET);
+
+ if (isFileUrl) {
+ input_item_node_t * const directoryNode = input_item_node_Create(directoryItem);
+ _p_mediaSource->tree->root = *directoryNode;
+ } else {
+ input_item_node_t * const directoryNode = input_item_node_Create(directoryItem);
+ input_item_node_AppendNode(&_p_mediaSource->tree->root, directoryNode);
+ input_item_Release(directoryItem);
+ _p_treeListenerID = vlc_media_tree_AddListener(_p_mediaSource->tree,
+ &treeCallbacks,
+ (__bridge void *)self,
+ NO);
+ }
}
return self;
}
@@ -282,6 +301,11 @@ static const char *const myFoldersDescription = "My Folders";
free(_p_mediaSource->tree);
free(_p_mediaSource);
_p_mediaSource = NULL;
+ } else if (_p_mediaSource->description == remoteBrowseDescription) {
+ _p_mediaSource->description = NULL;
+ vlc_media_tree_Release(_p_mediaSource->tree);
+ free(_p_mediaSource);
+ _p_mediaSource = NULL;
} else {
vlc_media_source_Release(_p_mediaSource);
}
=====================================
modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.h
=====================================
@@ -62,7 +62,7 @@ extern NSString * const VLCMediaSourceBaseDataSourceNodeChanged;
- (void)homeButtonAction:(id)sender;
- (void)pathControlAction:(id)sender;
-- (void)presentLocalFolderMrl:(NSString *)mrl;
+- (void)browseFolderByMrl:(NSString *)mrl;
@end
=====================================
modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.m
=====================================
@@ -650,13 +650,13 @@ referenceSizeForHeaderInSection:(NSInteger)section
_lanDeviceSnapshot = [self buildLANDeviceSnapshot];
}
if (self.viewMode == VLCLibraryGridViewModeSegment) {
- if (self.collectionView.dataSource == self) {
- const NSInteger index = [_mediaSources indexOfObject:aNotification.object];
- if (self.collectionView.numberOfSections > index) {
- [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:index]];
- } else {
- [self.collectionView reloadData];
- }
+ const NSInteger index = [_mediaSources indexOfObject:aNotification.object];
+ const BOOL inSync =
+ self.collectionView.numberOfSections == (NSInteger)_mediaSources.count;
+ if (self.collectionView.dataSource == self
+ && index != NSNotFound
+ && inSync) {
+ [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:index]];
} else {
[self.collectionView reloadData];
}
@@ -680,16 +680,23 @@ referenceSizeForHeaderInSection:(NSInteger)section
object:self];
}
-- (void)presentLocalFolderMrl:(NSString *)mrl
+- (void)browseFolderByMrl:(NSString *)mrl
{
vlc_preparser_t *p_preparser = getNetworkPreparser();
VLCMediaSource * const mediaSource =
- [[VLCMediaSource alloc] initWithLocalFolderMrl:mrl andPreparser:p_preparser];
+ [[VLCMediaSource alloc] initWithFolderMrl:mrl andPreparser:p_preparser];
if (mediaSource == nil) {
NSLog(@"Could not create valid media source for mrl: %@", mrl);
return;
}
- [self configureChildDataSourceWithNode:mediaSource.rootNode andMediaSource:mediaSource];
+ VLCInputNode * const entryNode = mediaSource.category == SD_CAT_LAN
+ ? mediaSource.rootNode.children.firstObject
+ : mediaSource.rootNode;
+ if (entryNode == nil) {
+ NSLog(@"No entry node for mrl: %@", mrl);
+ return;
+ }
+ [self configureChildDataSourceWithNode:entryNode andMediaSource:mediaSource];
[self reloadData];
}
=====================================
modules/gui/macosx/library/media-source/VLCMediaSourceDataSource.m
=====================================
@@ -65,11 +65,43 @@ NSString * const VLCMediaSourceDataSourceNodeChanged = @"VLCMediaSourceDataSourc
- (instancetype)initWithParentBaseDataSource:(VLCMediaSourceBaseDataSource *)parentBaseDataSource
{
self = [super init];
- if (self)
+ if (self) {
self.parentBaseDataSource = parentBaseDataSource;
+ NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
+ [notificationCenter addObserver:self
+ selector:@selector(mediaSourceChildrenChanged:)
+ name:VLCMediaSourceChildrenReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(mediaSourceChildrenChanged:)
+ name:VLCMediaSourceChildrenAdded
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(mediaSourceChildrenChanged:)
+ name:VLCMediaSourceChildrenRemoved
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(mediaSourceChildrenChanged:)
+ name:VLCMediaSourcePreparsingEnded
+ object:nil];
+ }
return self;
}
+- (void)dealloc
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+}
+
+- (void)mediaSourceChildrenChanged:(NSNotification *)notification
+{
+ if (notification.object != self.displayedMediaSource)
+ return;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self reloadData];
+ });
+}
+
- (dispatch_source_t)observeLocalUrl:(NSURL *)url
forVnodeEvents:(dispatch_source_vnode_flags_t)eventsFlags
withEventHandler:(dispatch_block_t)eventHandlerBlock
@@ -107,6 +139,12 @@ NSString * const VLCMediaSourceDataSourceNodeChanged = @"VLCMediaSourceDataSourc
NSParameterAssert(self.parentBaseDataSource);
if (self.parentBaseDataSource.mediaSourceMode == VLCMediaSourceModeLAN) {
NSURL * const nodeUrl = [NSURL URLWithString:nodeToDisplay.inputItem.MRL];
+
+ if (!nodeUrl.isFileURL) {
+ [self reloadData];
+ return;
+ }
+
NSError * const error =
[self.displayedMediaSource generateChildNodesForDirectoryNode:inputNode
withUrl:nodeUrl];
@@ -121,7 +159,7 @@ NSString * const VLCMediaSourceDataSourceNodeChanged = @"VLCMediaSourceDataSourc
const __weak typeof(self) weakSelf = self;
self.observedPathDispatchSource = [self observeLocalUrl:nodeUrl
- forVnodeEvents:DISPATCH_VNODE_WRITE |
+ forVnodeEvents:DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_DELETE |
DISPATCH_VNODE_RENAME
withEventHandler:^{
=====================================
modules/gui/macosx/menus/VLCMainMenu.h
=====================================
@@ -50,6 +50,7 @@
@property (readwrite, weak) IBOutlet NSMenuItem *open_disc;
@property (readwrite, weak) IBOutlet NSMenuItem *open_net;
@property (readwrite, weak) IBOutlet NSMenuItem *open_capture;
+ at property (readwrite, weak) IBOutlet NSMenuItem *connect_to_server;
@property (readwrite, weak) IBOutlet NSMenuItem *open_recent;
@property (readwrite, weak) IBOutlet NSMenuItem *close_window;
@property (readwrite, weak) IBOutlet NSMenuItem *convertandsave;
@@ -237,6 +238,7 @@
- (IBAction)intfOpenDisc:(id)sender;
- (IBAction)intfOpenNet:(id)sender;
- (IBAction)intfOpenCapture:(id)sender;
+- (IBAction)intfConnectToServer:(id)sender;
- (IBAction)savePlaylist:(id)sender;
- (IBAction)savePlayQueueToLibrary:(id)sender;
=====================================
modules/gui/macosx/menus/VLCMainMenu.m
=====================================
@@ -50,6 +50,7 @@
#import "preferences/VLCSimplePrefsController.h"
#import "windows/VLCAboutWindowController.h"
+#import "windows/VLCConnectToServerDialog.h"
#import "windows/VLCDetachedAudioWindow.h"
#import "windows/VLCOpenWindowController.h"
#import "windows/VLCErrorWindowController.h"
@@ -350,6 +351,7 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
[_open_disc setTitle: _NS("Open Disc...")];
[_open_net setTitle: _NS("Open Stream...")];
[_open_capture setTitle: _NS("Open Capture Device...")];
+ [_connect_to_server setTitle: _NS("Connect to Server...")];
[_open_recent setTitle: _NS("Open Recent")];
[_close_window setTitle: _NS("Close Window")];
[_convertandsave setTitle: _NS("Convert / Stream...")];
@@ -1336,6 +1338,12 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
[[VLCMain.sharedInstance open] openCapture];
}
+- (IBAction)intfConnectToServer:(id)sender
+{
+ VLCConnectToServerDialog * const dialog = [[VLCConnectToServerDialog alloc] init];
+ [dialog show];
+}
+
- (IBAction)savePlaylist:(id)sender
{
static dispatch_once_t once;
=====================================
modules/gui/macosx/windows/VLCConnectToServerDialog.h
=====================================
@@ -0,0 +1,33 @@
+/*****************************************************************************
+ * VLCConnectToServerDialog.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2026 VLC authors and VideoLAN
+ *
+ * Authors: Felix Paul Kühne <fkuehne -at- 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 <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at interface VLCConnectToServerDialog : NSObject
+
+- (void)show;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/windows/VLCConnectToServerDialog.m
=====================================
@@ -0,0 +1,197 @@
+/*****************************************************************************
+ * VLCConnectToServerDialog.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2026 VLC authors and VideoLAN
+ *
+ * Authors: Felix Paul Kühne <fkuehne -at- 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 "VLCConnectToServerDialog.h"
+
+#import "extensions/NSString+Helpers.h"
+#import "library/VLCLibraryWindow.h"
+#import "main/VLCMain.h"
+
+#import <vlc_modules.h>
+
+static NSString * const VLCConnectToServerRecentsDefaultsKey = @"VLCConnectToServerRecents";
+static const NSUInteger VLCConnectToServerMaxRecents = 8;
+
+ at interface VLCConnectToServerDialog () <NSComboBoxDataSource, NSComboBoxDelegate, NSControlTextEditingDelegate>
+{
+ NSComboBox *_addressField;
+ NSButton *_connectButton;
+ NSMutableArray<NSString *> *_recents;
+}
+ at end
+
+ at implementation VLCConnectToServerDialog
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ NSArray<NSString *> * const stored =
+ [NSUserDefaults.standardUserDefaults stringArrayForKey:VLCConnectToServerRecentsDefaultsKey];
+ _recents = stored ? [stored mutableCopy] : [NSMutableArray array];
+ }
+ return self;
+}
+
+- (NSView *)buildAccessoryView
+{
+ NSView * const content = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 420, 56)];
+
+ _addressField = [[NSComboBox alloc] initWithFrame:NSZeroRect];
+ _addressField.translatesAutoresizingMaskIntoConstraints = NO;
+ _addressField.placeholderString = @"smb://user@server.example.com/";
+ _addressField.usesDataSource = YES;
+ _addressField.dataSource = self;
+ _addressField.delegate = self;
+ _addressField.completes = YES;
+ [content addSubview:_addressField];
+
+ NSTextField * const hint = [NSTextField labelWithString:[self supportedSchemesHint]];
+ hint.translatesAutoresizingMaskIntoConstraints = NO;
+ hint.textColor = NSColor.secondaryLabelColor;
+ hint.font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize];
+ [content addSubview:hint];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [_addressField.topAnchor constraintEqualToAnchor:content.topAnchor],
+ [_addressField.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
+ [_addressField.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
+
+ [hint.topAnchor constraintEqualToAnchor:_addressField.bottomAnchor constant:6],
+ [hint.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
+ [hint.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
+ [hint.bottomAnchor constraintEqualToAnchor:content.bottomAnchor],
+ ]];
+
+ return content;
+}
+
+- (NSString *)supportedSchemesHint
+{
+ NSMutableArray<NSString *> * const schemes = [NSMutableArray array];
+ if (module_exists("webdav"))
+ [schemes addObjectsFromArray:@[@"webdav", @"webdavs"]];
+ if (module_exists("smb2"))
+ [schemes addObject:@"smb"];
+ if (module_exists("ftp"))
+ [schemes addObjectsFromArray:@[@"ftp", @"ftps", @"ftpes"]];
+ if (module_exists("sftp"))
+ [schemes addObject:@"sftp"];
+ if (module_exists("nfs"))
+ [schemes addObject:@"nfs"];
+
+ return [NSString stringWithFormat:_NS("Supported protocols: %@"),
+ [schemes componentsJoinedByString:@", "]];
+}
+
+- (void)show
+{
+ NSAlert * const alert = [[NSAlert alloc] init];
+ alert.messageText = _NS("Connect to Server");
+ alert.informativeText = _NS("Enter a server address to browse.");
+ _connectButton = [alert addButtonWithTitle:_NS("Connect")];
+ _connectButton.enabled = NO;
+ [alert addButtonWithTitle:_NS("Cancel")];
+ alert.accessoryView = [self buildAccessoryView];
+ alert.window.initialFirstResponder = _addressField;
+
+ if ([alert runModal] != NSAlertFirstButtonReturn)
+ return;
+
+ NSString * const mrl = [self currentMrl];
+ if (mrl == nil)
+ return;
+
+ [self rememberRecent:mrl];
+ [VLCMain.sharedInstance.libraryWindow browseFolderByMrl:mrl];
+}
+
+#pragma mark - combo box data source (recent servers)
+
+- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)comboBox
+{
+ return _recents.count;
+}
+
+- (id)comboBox:(NSComboBox *)comboBox objectValueForItemAtIndex:(NSInteger)index
+{
+ return _recents[index];
+}
+
+- (NSString *)comboBox:(NSComboBox *)comboBox completedString:(NSString *)string
+{
+ for (NSString * const entry in _recents) {
+ if ([entry.lowercaseString hasPrefix:string.lowercaseString])
+ return entry;
+ }
+ return nil;
+}
+
+#pragma mark - validation
+
+- (void)controlTextDidChange:(NSNotification *)notification
+{
+ [self updateConnectEnabled];
+}
+
+- (void)comboBoxSelectionDidChange:(NSNotification *)notification
+{
+ NSComboBox * const box = notification.object;
+ const NSInteger index = box.indexOfSelectedItem;
+ if (index >= 0 && index < (NSInteger)_recents.count)
+ box.stringValue = _recents[index];
+ [self updateConnectEnabled];
+}
+
+- (void)updateConnectEnabled
+{
+ _connectButton.enabled = [self currentMrl] != nil;
+}
+
+- (nullable NSString *)currentMrl
+{
+ NSString * const input = [_addressField.stringValue stringByTrimmingCharactersInSet:
+ NSCharacterSet.whitespaceAndNewlineCharacterSet];
+ if (input.length == 0)
+ return nil;
+
+ NSURL * const url = [NSURL URLWithString:input];
+ if (url.scheme.length == 0 || url.host.length == 0)
+ return nil;
+
+ return input;
+}
+
+#pragma mark - recent list
+
+- (void)rememberRecent:(NSString *)mrl
+{
+ [_recents removeObject:mrl];
+ [_recents insertObject:mrl atIndex:0];
+ while (_recents.count > VLCConnectToServerMaxRecents)
+ [_recents removeLastObject];
+
+ [NSUserDefaults.standardUserDefaults setObject:_recents
+ forKey:VLCConnectToServerRecentsDefaultsKey];
+}
+
+ at end
=====================================
po/POTFILES.in
=====================================
@@ -604,6 +604,8 @@ modules/gui/macosx/views/VLCVolumeSliderCell.h
modules/gui/macosx/views/VLCVolumeSliderCell.m
modules/gui/macosx/windows/VLCAboutWindowController.h
modules/gui/macosx/windows/VLCAboutWindowController.m
+modules/gui/macosx/windows/VLCConnectToServerDialog.h
+modules/gui/macosx/windows/VLCConnectToServerDialog.m
modules/gui/macosx/windows/VLCDetachedAudioWindow.h
modules/gui/macosx/windows/VLCDetachedAudioWindow.m
modules/gui/macosx/windows/VLCErrorWindowController.h
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0eb9ea48f22f849d529b9aaf9a74d457e54300e5...03f30a16fb3b2aad444db158d72ae2db176158c6
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0eb9ea48f22f849d529b9aaf9a74d457e54300e5...03f30a16fb3b2aad444db158d72ae2db176158c6
You're receiving this email because of your account on code.videolan.org.
More information about the vlc-commits
mailing list