[vlc-commits] macosx/media-source: add table view representation

Felix Paul Kühne git at videolan.org
Mon Jul 1 10:48:05 CEST 2019


vlc | branch: master | Felix Paul Kühne <felix at feepk.net> | Sun Jun 30 22:51:16 2019 +0200| [02e65ff064f77d04c35b4cd696b4bf877fe0d3fa] | committer: Felix Paul Kühne

macosx/media-source: add table view representation

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=02e65ff064f77d04c35b4cd696b4bf877fe0d3fa
---

 modules/gui/macosx/UI/VLCLibraryTableCellView.xib  |   3 -
 modules/gui/macosx/UI/VLCLibraryWindow.xib         |  74 ++++++++-
 .../gui/macosx/library/VLCLibraryTableCellView.h   |   6 +-
 .../gui/macosx/library/VLCLibraryTableCellView.m   |  53 ++++--
 modules/gui/macosx/library/VLCLibraryWindow.h      |   3 +
 modules/gui/macosx/library/VLCLibraryWindow.m      |   4 +
 .../media-source/VLCMediaSourceBaseDataSource.h    |   3 +
 .../media-source/VLCMediaSourceBaseDataSource.m    | 182 +++++++++++++++++++--
 .../macosx/media-source/VLCMediaSourceDataSource.h |   6 +-
 .../macosx/media-source/VLCMediaSourceDataSource.m | 103 +++++++++++-
 10 files changed, 399 insertions(+), 38 deletions(-)

diff --git a/modules/gui/macosx/UI/VLCLibraryTableCellView.xib b/modules/gui/macosx/UI/VLCLibraryTableCellView.xib
index 4e5a0199f8..542c6976fc 100644
--- a/modules/gui/macosx/UI/VLCLibraryTableCellView.xib
+++ b/modules/gui/macosx/UI/VLCLibraryTableCellView.xib
@@ -62,9 +62,6 @@
                         <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                         <font key="font" metaFont="system"/>
                     </buttonCell>
-                    <connections>
-                        <action selector="playInstantly:" target="c22-O7-iKe" id="Isu-g1-2RI"/>
-                    </connections>
                 </button>
             </subviews>
             <constraints>
diff --git a/modules/gui/macosx/UI/VLCLibraryWindow.xib b/modules/gui/macosx/UI/VLCLibraryWindow.xib
index 9cc979eeb5..a7be7373a2 100644
--- a/modules/gui/macosx/UI/VLCLibraryWindow.xib
+++ b/modules/gui/macosx/UI/VLCLibraryWindow.xib
@@ -520,12 +520,15 @@
                 <outlet property="clearPlaylistButton" destination="cih-xp-HmY" id="PoU-co-0kn"/>
                 <outlet property="clearPlaylistSeparator" destination="nAW-KH-ipk" id="Af9-fg-u7m"/>
                 <outlet property="controlsBar" destination="Uzf-Tf-H8x" id="n0G-92-F2Q"/>
+                <outlet property="gridVsListSegmentedControl" destination="7K7-4r-Swk" id="VoD-jF-46N"/>
                 <outlet property="librarySortButton" destination="Rja-6g-wNZ" id="FQ7-MU-hsk"/>
                 <outlet property="libraryTargetView" destination="iSp-bV-w6B" id="a94-ux-wUc"/>
                 <outlet property="mainSplitView" destination="u8g-jy-S4e" id="lI5-wR-kef"/>
                 <outlet property="mediaSourceCollectionView" destination="r7v-GI-W1U" id="3JJ-GU-o5o"/>
+                <outlet property="mediaSourceCollectionViewScrollView" destination="cFG-c9-cI9" id="QQq-Ql-uQ7"/>
                 <outlet property="mediaSourceHomeButton" destination="jfA-Vr-sQc" id="oLM-NF-rqe"/>
                 <outlet property="mediaSourcePathControl" destination="Rjk-Q6-FYy" id="G63-NM-Ekn"/>
+                <outlet property="mediaSourceTableView" destination="vpJ-Oz-Ebz" id="Hbo-Qw-JQd"/>
                 <outlet property="mediaSourceView" destination="eHd-Q9-F8D" id="gfU-Jp-eFr"/>
                 <outlet property="openMediaButton" destination="SWh-4E-Qtf" id="sIZ-xo-GLA"/>
                 <outlet property="playlistCounterTextField" destination="mbV-My-cm7" id="ZYU-Jq-Z8R"/>
@@ -649,7 +652,7 @@
                     <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                     <clipView key="contentView" id="dmB-cB-az6">
                         <rect key="frame" x="0.0" y="0.0" width="155" height="327"/>
-                        <autoresizingMask key="autoresizingMask"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" rowHeight="36" rowSizeStyle="large" viewBased="YES" id="dNP-8u-8iI">
                                 <rect key="frame" x="0.0" y="0.0" width="155" height="327"/>
@@ -709,7 +712,7 @@
                     <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                     <clipView key="contentView" id="qva-RZ-DvL">
                         <rect key="frame" x="0.0" y="0.0" width="154" height="327"/>
-                        <autoresizingMask key="autoresizingMask"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" viewBased="YES" id="LNt-ot-2wU">
                                 <rect key="frame" x="0.0" y="0.0" width="154" height="327"/>
@@ -769,7 +772,7 @@
                     <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                     <clipView key="contentView" id="xCC-h9-931">
                         <rect key="frame" x="0.0" y="0.0" width="197" height="327"/>
-                        <autoresizingMask key="autoresizingMask"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="4ll-T2-J16">
                                 <rect key="frame" x="0.0" y="0.0" width="197" height="327"/>
@@ -840,7 +843,7 @@
                     <rect key="frame" x="0.0" y="0.0" width="500" height="302"/>
                     <clipView key="contentView" id="2oa-WL-dxA">
                         <rect key="frame" x="0.0" y="0.0" width="500" height="302"/>
-                        <autoresizingMask key="autoresizingMask"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <subviews>
                             <collectionView id="QAt-jP-zE7">
                                 <rect key="frame" x="0.0" y="0.0" width="500" height="302"/>
@@ -874,6 +877,65 @@
             <rect key="frame" x="0.0" y="0.0" width="528" height="307"/>
             <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
             <subviews>
+                <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7WD-fy-WtJ">
+                    <rect key="frame" x="0.0" y="0.0" width="528" height="267"/>
+                    <clipView key="contentView" id="5co-vI-cEn">
+                        <rect key="frame" x="0.0" y="0.0" width="528" height="267"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" viewBased="YES" id="vpJ-Oz-Ebz">
+                                <rect key="frame" x="0.0" y="0.0" width="528" height="267"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <size key="intercellSpacing" width="3" height="2"/>
+                                <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+                                <tableColumns>
+                                    <tableColumn width="525" minWidth="40" maxWidth="1000" id="89Q-Wg-BdA">
+                                        <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
+                                            <font key="font" metaFont="smallSystem"/>
+                                            <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+                                        </tableHeaderCell>
+                                        <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="v9H-KJ-Zhv">
+                                            <font key="font" metaFont="system"/>
+                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                        </textFieldCell>
+                                        <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+                                        <prototypeCellViews>
+                                            <tableCellView id="prL-6m-HxA">
+                                                <rect key="frame" x="1" y="1" width="525" height="0.0"/>
+                                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                                <subviews>
+                                                    <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TVO-GX-uf2">
+                                                        <rect key="frame" x="0.0" y="-17" width="525" height="17"/>
+                                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+                                                        <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="LYl-7r-eAr">
+                                                            <font key="font" metaFont="system"/>
+                                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                                        </textFieldCell>
+                                                    </textField>
+                                                </subviews>
+                                                <connections>
+                                                    <outlet property="textField" destination="TVO-GX-uf2" id="2t6-OK-CMa"/>
+                                                </connections>
+                                            </tableCellView>
+                                        </prototypeCellViews>
+                                    </tableColumn>
+                                </tableColumns>
+                            </tableView>
+                        </subviews>
+                    </clipView>
+                    <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Xu2-Uw-wBR">
+                        <rect key="frame" x="0.0" y="-16" width="0.0" height="16"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </scroller>
+                    <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="UVE-ma-wE8">
+                        <rect key="frame" x="-16" y="0.0" width="16" height="0.0"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </scroller>
+                </scrollView>
                 <scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cFG-c9-cI9">
                     <rect key="frame" x="0.0" y="0.0" width="528" height="267"/>
                     <clipView key="contentView" id="tI4-x3-55j">
@@ -918,10 +980,14 @@
                 </button>
             </subviews>
             <constraints>
+                <constraint firstAttribute="bottom" secondItem="7WD-fy-WtJ" secondAttribute="bottom" id="05c-7d-nw6"/>
                 <constraint firstItem="Rjk-Q6-FYy" firstAttribute="leading" secondItem="jfA-Vr-sQc" secondAttribute="trailing" constant="10" id="1OY-9N-uuV"/>
+                <constraint firstItem="7WD-fy-WtJ" firstAttribute="leading" secondItem="eHd-Q9-F8D" secondAttribute="leading" id="233-Yb-V4O"/>
                 <constraint firstAttribute="bottom" secondItem="cFG-c9-cI9" secondAttribute="bottom" id="DjO-Ru-7sa"/>
                 <constraint firstItem="cFG-c9-cI9" firstAttribute="top" secondItem="Rjk-Q6-FYy" secondAttribute="bottom" constant="10" id="Lji-yk-wiI"/>
+                <constraint firstAttribute="trailing" secondItem="7WD-fy-WtJ" secondAttribute="trailing" id="PnR-KD-94A"/>
                 <constraint firstItem="Rjk-Q6-FYy" firstAttribute="top" secondItem="eHd-Q9-F8D" secondAttribute="top" constant="10" id="Sf6-jz-ool"/>
+                <constraint firstItem="7WD-fy-WtJ" firstAttribute="top" secondItem="Rjk-Q6-FYy" secondAttribute="bottom" constant="10" id="Xao-NR-Utd"/>
                 <constraint firstItem="jfA-Vr-sQc" firstAttribute="centerY" secondItem="Rjk-Q6-FYy" secondAttribute="centerY" id="aFD-99-R3a"/>
                 <constraint firstAttribute="trailing" secondItem="Rjk-Q6-FYy" secondAttribute="trailing" constant="10" id="c3j-mQ-afF"/>
                 <constraint firstItem="jfA-Vr-sQc" firstAttribute="leading" secondItem="eHd-Q9-F8D" secondAttribute="leading" constant="10" id="saP-eI-VDF"/>
diff --git a/modules/gui/macosx/library/VLCLibraryTableCellView.h b/modules/gui/macosx/library/VLCLibraryTableCellView.h
index 0cae354a8e..202ed2b90a 100644
--- a/modules/gui/macosx/library/VLCLibraryTableCellView.h
+++ b/modules/gui/macosx/library/VLCLibraryTableCellView.h
@@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
 @class VLCImageView;
 @class VLCTrackingView;
 @class VLCMediaLibraryMediaItem;
+ at class VLCInputItem;
 
 @interface VLCLibraryTableCellView : NSTableCellView
 
@@ -36,9 +37,8 @@ NS_ASSUME_NONNULL_BEGIN
 @property (readwrite, assign) IBOutlet NSTextField *primaryTitleTextField;
 @property (readwrite, assign) IBOutlet VLCImageView *representedImageView;
 @property (readwrite, assign) IBOutlet NSButton *playInstantlyButton;
- at property (readwrite, assign, nonatomic) VLCMediaLibraryMediaItem *representedMediaItem;
-
-- (IBAction)playInstantly:(id)sender;
+ at property (readwrite, strong, nonatomic) VLCMediaLibraryMediaItem *representedMediaItem;
+ at property (readwrite, strong, nonatomic) VLCInputItem *representedInputItem;
 
 @end
 
diff --git a/modules/gui/macosx/library/VLCLibraryTableCellView.m b/modules/gui/macosx/library/VLCLibraryTableCellView.m
index b12e78dcf8..9e5d9f51e6 100644
--- a/modules/gui/macosx/library/VLCLibraryTableCellView.m
+++ b/modules/gui/macosx/library/VLCLibraryTableCellView.m
@@ -27,12 +27,8 @@
 #import "main/VLCMain.h"
 #import "library/VLCLibraryController.h"
 #import "library/VLCLibraryDataTypes.h"
-
- at interface VLCLibraryTableCellView ()
-{
-    VLCLibraryController *_libraryController;
-}
- at end
+#import "library/VLCInputItem.h"
+#import "playlist/VLCPlaylistController.h"
 
 @implementation VLCLibraryTableCellView
 
@@ -58,16 +54,53 @@
 - (void)setRepresentedMediaItem:(VLCMediaLibraryMediaItem *)representedMediaItem
 {
     _representedMediaItem = representedMediaItem;
+
+    self.trackingView.viewToHide = self.playInstantlyButton;
+    self.playInstantlyButton.action = @selector(playMediaItemInstantly:);
+    self.playInstantlyButton.target = self;
+}
+
+- (void)setRepresentedInputItem:(VLCInputItem *)representedInputItem
+{
+    _representedInputItem = representedInputItem;
+
+    self.singlePrimaryTitleTextField.hidden = NO;
+    self.singlePrimaryTitleTextField.stringValue = _representedInputItem.name;
+
+    NSURL *artworkURL = _representedInputItem.artworkURL;
+    NSImage *placeholderImage = [self imageForInputItem];
+    if (artworkURL) {
+        [self.representedImageView setImageURL:artworkURL placeholderImage:placeholderImage];
+    } else {
+        self.representedImageView.image = placeholderImage;
+    }
+
     self.trackingView.viewToHide = self.playInstantlyButton;
+    self.playInstantlyButton.action = @selector(playInputItemInstantly:);
+    self.playInstantlyButton.target = self;
 }
 
-- (IBAction)playInstantly:(id)sender
+- (NSImage *)imageForInputItem
 {
-    if (!_libraryController) {
-        _libraryController = [[VLCMain sharedInstance] libraryController];
+    NSImage *image;
+    if (_representedInputItem.inputType == ITEM_TYPE_DIRECTORY) {
+        image = [NSImage imageNamed:NSImageNameFolder];
+    }
+
+    if (!image) {
+        image = [NSImage imageNamed: @"noart.png"];
     }
+    return image;
+}
 
-    [_libraryController appendItemToPlaylist:_representedMediaItem playImmediately:YES];
+- (void)playMediaItemInstantly:(id)sender
+{
+    [[[VLCMain sharedInstance] libraryController] appendItemToPlaylist:_representedMediaItem playImmediately:YES];
+}
+
+- (void)playInputItemInstantly:(id)sender
+{
+    [[[VLCMain sharedInstance] playlistController] addInputItem:_representedInputItem.vlcInputItem atPosition:-1 startPlayback:YES];
 }
 
 @end
diff --git a/modules/gui/macosx/library/VLCLibraryWindow.h b/modules/gui/macosx/library/VLCLibraryWindow.h
index d3b6e5c0a3..ce72c4ed64 100644
--- a/modules/gui/macosx/library/VLCLibraryWindow.h
+++ b/modules/gui/macosx/library/VLCLibraryWindow.h
@@ -36,6 +36,7 @@ NS_ASSUME_NONNULL_BEGIN
 @interface VLCLibraryWindow : VLCVideoWindowCommon
 
 @property (readwrite, weak) IBOutlet NSSegmentedControl *segmentedTitleControl;
+ at property (readwrite, weak) IBOutlet NSSegmentedControl *gridVsListSegmentedControl;
 @property (readwrite, weak) IBOutlet NSSplitView *mainSplitView;
 @property (readwrite, weak) IBOutlet NSStackView *videoLibraryStackView;
 @property (readwrite, strong) IBOutlet NSView *playlistView;
@@ -49,6 +50,8 @@ NS_ASSUME_NONNULL_BEGIN
 @property (readwrite, weak) IBOutlet NSView *mediaSourceView;
 @property (readwrite, weak) IBOutlet NSButton *mediaSourceHomeButton;
 @property (readwrite, weak) IBOutlet NSPathControl *mediaSourcePathControl;
+ at property (readwrite, weak) IBOutlet NSTableView *mediaSourceTableView;
+ at property (readwrite, weak) IBOutlet NSScrollView *mediaSourceCollectionViewScrollView;
 @property (readwrite, weak) IBOutlet NSView *libraryTargetView;
 @property (readwrite, weak) IBOutlet NSTableView *playlistTableView;
 @property (readwrite, weak) IBOutlet NSTextField *upNextLabel;
diff --git a/modules/gui/macosx/library/VLCLibraryWindow.m b/modules/gui/macosx/library/VLCLibraryWindow.m
index 2d769366e8..79d4b2d8ed 100644
--- a/modules/gui/macosx/library/VLCLibraryWindow.m
+++ b/modules/gui/macosx/library/VLCLibraryWindow.m
@@ -237,8 +237,12 @@ static int ShowController(vlc_object_t *p_this, const char *psz_variable,
 
     _mediaSourceDataSource = [[VLCMediaSourceBaseDataSource alloc] init];
     _mediaSourceDataSource.collectionView = _mediaSourceCollectionView;
+    _mediaSourceDataSource.collectionViewScrollView = _mediaSourceCollectionViewScrollView;
     _mediaSourceDataSource.homeButton = _mediaSourceHomeButton;
     _mediaSourceDataSource.pathControl = _mediaSourcePathControl;
+    _mediaSourceDataSource.gridVsListSegmentedControl = _gridVsListSegmentedControl;
+    _mediaSourceTableView.rowHeight = VLCLibraryWindowLargeRowHeight;
+    _mediaSourceDataSource.tableView = _mediaSourceTableView;
     [_mediaSourceDataSource setupViews];
 
     self.upNextLabel.font = [NSFont VLClibrarySectionHeaderFont];
diff --git a/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.h b/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.h
index 7df831c4e2..df6f4044a3 100644
--- a/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.h
+++ b/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.h
@@ -32,6 +32,9 @@ NS_ASSUME_NONNULL_BEGIN
 @interface VLCMediaSourceBaseDataSource : NSObject
 
 @property (readwrite) NSCollectionView *collectionView;
+ at property (readwrite) NSScrollView *collectionViewScrollView;
+ at property (readwrite) NSTableView *tableView;
+ at property (readwrite) NSSegmentedControl *gridVsListSegmentedControl;
 @property (readwrite) NSButton *homeButton;
 @property (readwrite) NSPathControl *pathControl;
 @property (readwrite, nonatomic) VLCMediaSourceMode mediaSourceMode;
diff --git a/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.m b/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.m
index 8813eb0c8a..83cf79a7a4 100644
--- a/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.m
+++ b/modules/gui/macosx/media-source/VLCMediaSourceBaseDataSource.m
@@ -31,12 +31,17 @@
 #import "main/VLCMain.h"
 #import "views/VLCImageView.h"
 #import "library/VLCInputItem.h"
+#import "library/VLCLibraryTableCellView.h"
 #import "extensions/NSString+Helpers.h"
 
- at interface VLCMediaSourceBaseDataSource () <NSCollectionViewDataSource, NSCollectionViewDelegate>
+NSString *VLCMediaSourceTableViewCellIdentifier = @"VLCMediaSourceTableViewCellIdentifier";
+
+ at interface VLCMediaSourceBaseDataSource () <NSCollectionViewDataSource, NSCollectionViewDelegate, NSTableViewDelegate, NSTableViewDataSource>
 {
     NSArray *_mediaSources;
     VLCMediaSourceDataSource *_childDataSource;
+    NSArray *_discoveredLANdevices;
+    BOOL _gridViewMode;
 }
 @end
 
@@ -69,6 +74,8 @@
     [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
+#pragma mark - view and model state management
+
 - (void)setupViews
 {
     self.collectionView.dataSource = self;
@@ -79,6 +86,15 @@
     self.homeButton.action = @selector(homeButtonAction:);
     self.homeButton.target = self;
     self.pathControl.URL = nil;
+
+    self.gridVsListSegmentedControl.action = @selector(switchGripOrListMode:);
+    self.gridVsListSegmentedControl.target = self;
+    self.gridVsListSegmentedControl.selectedSegment = 0;
+
+    self.tableView.dataSource = self;
+    self.tableView.delegate = self;
+    self.tableView.hidden = YES;
+    _gridViewMode = YES;
 }
 
 - (void)loadMediaSources
@@ -109,6 +125,8 @@
     [self homeButtonAction:nil];
 }
 
+#pragma mark - collection view data source
+
 - (NSInteger)numberOfSectionsInCollectionView:(NSCollectionView *)collectionView
 {
     if (_mediaSourceMode == VLCMediaSourceModeLAN) {
@@ -168,7 +186,6 @@
 
     VLCMediaSource *mediaSource;
     VLCInputNode *childNode;
-    _childDataSource = [[VLCMediaSourceDataSource alloc] init];
 
     if (_mediaSourceMode == VLCMediaSourceModeLAN) {
         mediaSource = _mediaSources[indexPath.section];
@@ -180,25 +197,155 @@
         childNode = mediaSource.rootNode;
     }
 
-    VLCInputItem *childRootInput = childNode.inputItem;
-    self.pathControl.URL = [NSURL URLWithString:[NSString stringWithFormat:@"vlc://%@", [childRootInput.name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]];
+    [self configureChildDataSourceWithNode:childNode andMediaSource:mediaSource];
+
+    [self reloadData];
+}
+
+#pragma mark - table view data source and delegation
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+    if (_mediaSourceMode == VLCMediaSourceModeLAN) {
+        /* for LAN, we don't show the root items but the top items, which may change any time through a callback
+         * so we don't run into conflicts, we compile a list of the currently known here and propose that
+         * as the truth to the table view. For collection view, we use sections which can be reloaded individually,
+         * so the problem is well hidden and does not need this work-around */
+        _discoveredLANdevices = nil;
+        NSMutableArray *currentDevices;
+        @synchronized (_mediaSources) {
+            NSInteger mediaSourceCount = _mediaSources.count;
+            currentDevices = [[NSMutableArray alloc] initWithCapacity:mediaSourceCount];
+            for (NSUInteger x = 0; x < mediaSourceCount; x++) {
+                VLCMediaSource *mediaSource = _mediaSources[x];
+                VLCInputNode *rootNode = mediaSource.rootNode;
+                [currentDevices addObjectsFromArray:rootNode.children];
+            }
+        }
+        _discoveredLANdevices = [currentDevices copy];
+        return _discoveredLANdevices.count;
+    }
+
+    return _mediaSources.count;
+}
+
+- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
+{
+    VLCLibraryTableCellView *cellView = [tableView makeViewWithIdentifier:VLCMediaSourceTableViewCellIdentifier owner:self];
+
+    if (cellView == nil) {
+        /* the following code saves us an instance of NSViewController which we don't need */
+        NSNib *nib = [[NSNib alloc] initWithNibNamed:@"VLCLibraryTableCellView" bundle:nil];
+        NSArray *topLevelObjects;
+        if (![nib instantiateWithOwner:self topLevelObjects:&topLevelObjects]) {
+            NSAssert(1, @"Failed to load nib file to show audio library items");
+            return nil;
+        }
+
+        for (id topLevelObject in topLevelObjects) {
+            if ([topLevelObject isKindOfClass:[VLCLibraryTableCellView class]]) {
+                cellView = topLevelObject;
+                break;
+            }
+        }
+        cellView.identifier = VLCMediaSourceTableViewCellIdentifier;
+    }
+    cellView.primaryTitleTextField.hidden = YES;
+    cellView.secondaryTitleTextField.hidden = YES;
+    cellView.singlePrimaryTitleTextField.hidden = NO;
+
+    if (_mediaSourceMode == VLCMediaSourceModeLAN) {
+        VLCInputNode *currentNode = _discoveredLANdevices[row];
+        VLCInputItem *currentNodeInput = currentNode.inputItem;
+
+        NSURL *artworkURL = currentNodeInput.artworkURL;
+        NSImage *placeholder = [NSImage imageNamed:@"NXdefaultappicon"];
+        if (artworkURL) {
+            [cellView.representedImageView setImageURL:artworkURL placeholderImage:placeholder];
+        } else {
+            cellView.representedImageView.image = placeholder;
+        }
+
+        cellView.singlePrimaryTitleTextField.stringValue = currentNodeInput.name;
+    } else {
+        VLCMediaSource *mediaSource = _mediaSources[row];
+        cellView.singlePrimaryTitleTextField.stringValue = mediaSource.mediaSourceDescription;
+        cellView.representedImageView.image = [NSImage imageNamed:@"NXFollow"];
+    }
+
+    return cellView;
+}
+
+- (void)tableViewSelectionDidChange:(NSNotification *)notification
+{
+    NSInteger selectedRow = self.tableView.selectedRow;
+    if (selectedRow < 0) {
+        return;
+    }
+
+    VLCMediaSource *mediaSource = _mediaSources[selectedRow];;
+    VLCInputNode *childNode;
+    if (_mediaSourceMode == VLCMediaSourceModeLAN) {
+        childNode = _discoveredLANdevices[selectedRow];
+    } else {
+        childNode = mediaSource.rootNode;
+    }
+    [self configureChildDataSourceWithNode:childNode andMediaSource:mediaSource];
+
+    [self reloadData];
+}
+
+#pragma mark - glue code
+
+- (void)configureChildDataSourceWithNode:(VLCInputNode *)node andMediaSource:(VLCMediaSource *)mediaSource
+{
+    _childDataSource = [[VLCMediaSourceDataSource alloc] init];
+
+    VLCInputItem *nodeInput = node.inputItem;
+    self.pathControl.URL = [NSURL URLWithString:[NSString stringWithFormat:@"vlc://%@", [nodeInput.name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]];
 
     _childDataSource.displayedMediaSource = mediaSource;
-    _childDataSource.nodeToDisplay = childNode;
+    _childDataSource.nodeToDisplay = node;
     _childDataSource.collectionView = self.collectionView;
     _childDataSource.pathControl = self.pathControl;
+    _childDataSource.tableView = self.tableView;
+    [_childDataSource setupViews];
 
     self.collectionView.dataSource = _childDataSource;
     self.collectionView.delegate = _childDataSource;
-    [self.collectionView reloadData];
+
+    self.tableView.dataSource = _childDataSource;
+    self.tableView.delegate = _childDataSource;
 }
 
-- (IBAction)homeButtonAction:(id)sender
+#pragma mark - user interaction with generic buttons
+
+- (void)homeButtonAction:(id)sender
 {
     self.collectionView.dataSource = self;
     self.collectionView.delegate = self;
-    [self.collectionView reloadData];
+    self.tableView.dataSource = self;
+    self.tableView.delegate = self;
+
     _childDataSource = nil;
+
+    [self reloadData];
+}
+
+- (void)switchGripOrListMode:(id)sender
+{
+    _gridViewMode = !_gridViewMode;
+    _childDataSource.gridViewMode = _gridViewMode;
+
+    if (_gridViewMode) {
+        self.collectionViewScrollView.hidden = NO;
+        self.tableView.hidden = YES;
+        [self.collectionView reloadData];
+    } else {
+        self.collectionViewScrollView.hidden = YES;
+        self.tableView.hidden = NO;
+        [self.tableView reloadData];
+    }
 }
 
 #pragma mark - VLCMediaSource Delegation
@@ -223,11 +370,24 @@
 
 - (void)reloadDataForNotification:(NSNotification *)aNotification
 {
-    if (self.collectionView.dataSource == self) {
-        NSInteger index = [_mediaSources indexOfObject:aNotification.object];
-        [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:index]];
+    if (_gridViewMode) {
+        if (self.collectionView.dataSource == self) {
+            NSInteger index = [_mediaSources indexOfObject:aNotification.object];
+            [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:index]];
+        } else {
+            [self.collectionView reloadData];
+        }
     } else {
+        [self.tableView reloadData];
+    }
+}
+
+- (void)reloadData
+{
+    if (_gridViewMode) {
         [self.collectionView reloadData];
+    } else {
+        [self.tableView reloadData];
     }
 }
 
diff --git a/modules/gui/macosx/media-source/VLCMediaSourceDataSource.h b/modules/gui/macosx/media-source/VLCMediaSourceDataSource.h
index f7e3b02adf..bea78c6be7 100644
--- a/modules/gui/macosx/media-source/VLCMediaSourceDataSource.h
+++ b/modules/gui/macosx/media-source/VLCMediaSourceDataSource.h
@@ -27,12 +27,16 @@ NS_ASSUME_NONNULL_BEGIN
 @class VLCInputNode;
 @class VLCMediaSource;
 
- at interface VLCMediaSourceDataSource : NSObject <NSCollectionViewDataSource, NSCollectionViewDelegate>
+ at interface VLCMediaSourceDataSource : NSObject <NSCollectionViewDataSource, NSCollectionViewDelegate, NSTableViewDelegate, NSTableViewDataSource>
 
 @property (readwrite, retain) VLCMediaSource *displayedMediaSource;
 @property (readwrite, retain, nonatomic) VLCInputNode *nodeToDisplay;
 @property (readwrite, assign) NSCollectionView *collectionView;
+ at property (readwrite, assign) NSTableView *tableView;
 @property (readwrite) NSPathControl *pathControl;
+ at property (readwrite) BOOL gridViewMode;
+
+- (void)setupViews;
 
 @end
 
diff --git a/modules/gui/macosx/media-source/VLCMediaSourceDataSource.m b/modules/gui/macosx/media-source/VLCMediaSourceDataSource.m
index 4710a1c4d3..3cc116955b 100644
--- a/modules/gui/macosx/media-source/VLCMediaSourceDataSource.m
+++ b/modules/gui/macosx/media-source/VLCMediaSourceDataSource.m
@@ -23,6 +23,7 @@
 #import "VLCMediaSourceDataSource.h"
 
 #import "library/VLCInputItem.h"
+#import "library/VLCLibraryTableCellView.h"
 #import "media-source/VLCMediaSourceCollectionViewItem.h"
 #import "media-source/VLCMediaSource.h"
 #import "main/VLCMain.h"
@@ -33,7 +34,6 @@
 @interface VLCMediaSourceDataSource()
 {
     VLCInputItem *_childRootInput;
-    VLCMediaSourceDataSource *_childDataSource;
 }
 @end
 
@@ -47,6 +47,14 @@
     [self.displayedMediaSource preparseInputItemWithinTree:_childRootInput];
 }
 
+- (void)setupViews
+{
+    [self.tableView setDoubleAction:@selector(tableViewAction:)];
+    [self.tableView setTarget:self];
+}
+
+#pragma mark - collection view data source and delegation
+
 - (NSInteger)numberOfSectionsInCollectionView:(NSCollectionView *)collectionView
 {
     return 1;
@@ -86,17 +94,100 @@
     VLCInputNode *rootNode = self.nodeToDisplay;
     NSArray *nodeChildren = rootNode.children;
     VLCInputNode *childNode = nodeChildren[indexPath.item];
+
+    [self performActionForNode:childNode allowPlayback:YES];
+}
+
+#pragma mark - table view data source and delegation
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+    if (_nodeToDisplay) {
+        return _nodeToDisplay.numberOfChildren;
+    }
+
+    return 0;
+}
+
+- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
+{
+    VLCLibraryTableCellView *cellView = [tableView makeViewWithIdentifier:@"VLCMediaSourceTableViewCellIdentifier" owner:self];
+
+    if (cellView == nil) {
+        /* the following code saves us an instance of NSViewController which we don't need */
+        NSNib *nib = [[NSNib alloc] initWithNibNamed:@"VLCLibraryTableCellView" bundle:nil];
+        NSArray *topLevelObjects;
+        if (![nib instantiateWithOwner:self topLevelObjects:&topLevelObjects]) {
+            NSAssert(1, @"Failed to load nib file to show audio library items");
+            return nil;
+        }
+
+        for (id topLevelObject in topLevelObjects) {
+            if ([topLevelObject isKindOfClass:[VLCLibraryTableCellView class]]) {
+                cellView = topLevelObject;
+                break;
+            }
+        }
+        cellView.identifier = @"VLCMediaSourceTableViewCellIdentifier";
+    }
+
+    VLCInputNode *rootNode = _nodeToDisplay;
+    NSArray *nodeChildren = rootNode.children;
+    VLCInputNode *childNode = nodeChildren[row];
     VLCInputItem *childRootInput = childNode.inputItem;
+    cellView.representedInputItem = childRootInput;
+
+    return cellView;
+}
+
+- (void)tableViewSelectionDidChange:(NSNotification *)notification
+{
+    NSInteger selectedIndex = self.tableView.selectedRow;
+    if (selectedIndex < 0) {
+        return;
+    }
+    VLCInputNode *rootNode = self.nodeToDisplay;
+    NSArray *nodeChildren = rootNode.children;
+    VLCInputNode *childNode = nodeChildren[selectedIndex];
+
+    [self performActionForNode:childNode allowPlayback:NO];
+}
+
+- (void)tableViewAction:(id)sender
+{
+    NSInteger selectedIndex = self.tableView.selectedRow;
+    if (selectedIndex < 0) {
+        return;
+    }
+
+    VLCInputNode *rootNode = self.nodeToDisplay;
+    NSArray *nodeChildren = rootNode.children;
+    VLCInputNode *childNode = nodeChildren[selectedIndex];
+
+    [self performActionForNode:childNode allowPlayback:YES];
+}
+
+#pragma mark - generic actions
+
+- (void)performActionForNode:(VLCInputNode *)node allowPlayback:(BOOL)allowPlayback
+{
+    VLCInputItem *childRootInput = node.inputItem;
 
     if (childRootInput.inputType == ITEM_TYPE_DIRECTORY || childRootInput.inputType == ITEM_TYPE_NODE) {
         self.pathControl.URL = [NSURL URLWithString:[self.pathControl.URL.path stringByAppendingPathComponent:[childRootInput.name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]];
-        self.nodeToDisplay = childNode;
-        [self.collectionView reloadData];
-    } else if (childRootInput.inputType == ITEM_TYPE_FILE) {
+        self.nodeToDisplay = node;
+        [self reloadData];
+    } else if (childRootInput.inputType == ITEM_TYPE_FILE && allowPlayback) {
         [[[VLCMain sharedInstance] playlistController] addInputItem:childRootInput.vlcInputItem atPosition:-1 startPlayback:YES];
+    }
+}
+
+- (void)reloadData
+{
+    if (_gridViewMode) {
+        [self.collectionView reloadData];
     } else {
-        NSAssert(1, @"unhandled input type when browsing media source hierarchy %i", childRootInput.inputType);
-        msg_Warn(getIntf(), "unhandled input type when browsing media source hierarchy %i", childRootInput.inputType);
+        [self.tableView reloadData];
     }
 }
 



More information about the vlc-commits mailing list