VLCPlaylistViewController.m 53.2 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * VLCPlaylistViewController.m
 * VLC for iOS
 *****************************************************************************
Felix Paul Kühne's avatar
Felix Paul Kühne committed
5
 * Copyright (c) 2013-2014 VideoLAN. All rights reserved.
6
7
8
9
10
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Gleb Pinigin <gpinigin # gmail.com>
 *          Tamas Timar <ttimar.vlc # gmail.com>
Carola's avatar
Carola committed
11
 *          Carola Nitz <nitz.carola # gmail.com>
12
13
14
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
Felix Paul Kühne's avatar
Felix Paul Kühne committed
15
16

#import "VLCPlaylistViewController.h"
17
#import "VLCMovieViewController.h"
18
#import "VLCPlaylistTableViewCell.h"
19
#import "VLCPlaylistCollectionViewCell.h"
Romain Goyet's avatar
Romain Goyet committed
20
#import "UINavigationController+Theme.h"
21
#import "NSString+SupportedMedia.h"
22
#import "VLCBugreporter.h"
23
#import "VLCAppDelegate.h"
24
#import "UIBarButtonItem+Theme.h"
25
#import "VLCFirstStepsViewController.h"
Carola's avatar
Carola committed
26
27
28
#import "VLCFolderCollectionViewFlowLayout.h"
#import "LXReorderableCollectionViewFlowLayout.h"
#import "VLCAlertView.h"
29

30
31
32
/* prefs keys */
static NSString *kDisplayedFirstSteps = @"Did we display the first steps tutorial?";

33
@implementation EmptyLibraryView
34
35
36
37
38
39
40
41
42
43

- (IBAction)learnMore:(id)sender
{
    UIViewController *firstStepsVC = [[VLCFirstStepsViewController alloc] initWithNibName:nil bundle:nil];
    UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:firstStepsVC];
    navCon.modalPresentationStyle = UIModalPresentationFormSheet;
    [navCon loadTheme];
    [self.window.rootViewController presentViewController:navCon animated:YES completion:nil];
}

44
45
@end

46
@interface VLCPlaylistViewController () <VLCFolderCollectionViewDelegateFlowLayout, LXReorderableCollectionViewDataSource, LXReorderableCollectionViewDelegateFlowLayout, UITableViewDataSource, UITableViewDelegate, MLMediaLibrary, VLCMediaListDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
47
    NSMutableArray *_foundMedia;
48
    VLCLibraryMode _libraryMode;
Carola's avatar
Carola committed
49
    VLCLibraryMode _previousLibraryMode;
50
    UIBarButtonItem *_menuButton;
Carola's avatar
Carola committed
51
52
53
54
55
    NSMutableArray *_indexPaths;
    id _folderObject;
    VLCFolderCollectionViewFlowLayout *_folderLayout;
    LXReorderableCollectionViewFlowLayout *_reorderLayout;
    BOOL inFolder;
56
    UILongPressGestureRecognizer *_longPressGestureRecognizer;
57
58
59
    NSInteger _mediaToPlayIndex;
    VLCMediaList *_list;
    NSArray *_tracks;
60
61
62
63

    NSMutableArray *_searchData;
    UISearchBar *_searchBar;
    UISearchDisplayController *_searchDisplayController;
64
65

    BOOL _usingTableViewToShowData;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
66
}
67
68

@property (nonatomic, strong) UITableView *tableView;
69
@property (nonatomic, strong) UICollectionView *collectionView;
70
71
@property (nonatomic, strong) EmptyLibraryView *emptyLibraryView;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
72
73
74
75
@end

@implementation VLCPlaylistViewController

76
77
78
79
80
81
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults registerDefaults:@{kDisplayedFirstSteps : [NSNumber numberWithBool:NO]}];
}

82
83
- (void)loadView
{
84
85
86
    _usingTableViewToShowData = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone;

    if (_usingTableViewToShowData) {
87
        _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
88
        _tableView.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
89
90
        _tableView.rowHeight = [VLCPlaylistTableViewCell heightOfCell];
        _tableView.separatorColor = [UIColor colorWithWhite:.122 alpha:1.];
91
92
        _tableView.delegate = self;
        _tableView.dataSource = self;
93
        _tableView.opaque = YES;
94
95
        self.view = _tableView;
    } else {
Carola's avatar
Carola committed
96
97
        _folderLayout = [[VLCFolderCollectionViewFlowLayout alloc] init];
        _collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:_folderLayout];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
98
99
100
101
        _collectionView.alwaysBounceVertical = YES;
        _collectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
102
103
        _collectionView.opaque = YES;
        _collectionView.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
104
        self.view = _collectionView;
105
106
        _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_collectionViewHandleLongPressGesture:)];
        [_collectionView addGestureRecognizer:_longPressGestureRecognizer];
107
        if (SYSTEM_RUNS_IOS7_OR_LATER)
108
            [_collectionView registerNib:[UINib nibWithNibName:@"VLCFuturePlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
109
        else
110
            [_collectionView registerNib:[UINib nibWithNibName:@"VLCPlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
111
        self.view.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
112
113
    }

114
    _libraryMode = VLCLibraryModeAllFiles;
115

116
117
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.emptyLibraryView = [[[NSBundle mainBundle] loadNibNamed:@"VLCEmptyLibraryView" owner:self options:nil] lastObject];
118
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
119
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.numberOfLines = 0;
120
121
122
123
}

#pragma mark -

Felix Paul Kühne's avatar
Felix Paul Kühne committed
124
125
126
- (void)viewDidLoad
{
    [super viewDidLoad];
127
    self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
128
129
    _menuButton = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(leftButtonAction:)];
    self.navigationItem.leftBarButtonItem = _menuButton;
130

Felix Paul Kühne's avatar
Felix Paul Kühne committed
131
    if (SYSTEM_RUNS_IOS7_OR_LATER)
132
133
134
135
136
137
138
        self.editButtonItem.tintColor = [UIColor whiteColor];
    else {
        [self.editButtonItem setBackgroundImage:[UIImage imageNamed:@"button"]
                                       forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self.editButtonItem setBackgroundImage:[UIImage imageNamed:@"buttonHighlight"]
                                       forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
139

140
141
142
    _emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", @"");
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.text = NSLocalizedString(@"EMPTY_LIBRARY_LONG", @"");
    [_emptyLibraryView.emptyLibraryLongDescriptionLabel sizeToFit];
143
    [_emptyLibraryView.learnMoreButton setTitle:NSLocalizedString(@"BUTTON_LEARN_MORE", @"") forState:UIControlStateNormal];
Carola's avatar
Carola committed
144
145
    UIBarButtonItem *createFolderItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
    [self setToolbarItems:@[createFolderItem, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_RENAME", @"") target:self andSelector:@selector(renameSelection)], [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteSelection)]]];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
146
    self.navigationController.toolbar.barStyle = UIBarStyleBlack;
147

148
149
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
        self.navigationController.toolbar.tintColor = [UIColor whiteColor];
150
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
151
152
    } else
        [self.navigationController.toolbar setBackgroundImage:[UIImage imageNamed:@"bottomBlackBar"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
153

154
    if (_usingTableViewToShowData) {
155
156
157
158
159
160
161
162
163
164
165
        _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
        UINavigationBar *navBar = self.navigationController.navigationBar;
        _searchBar.barTintColor = navBar.barTintColor;
        _searchBar.tintColor = navBar.tintColor;
        _searchBar.translucent = navBar.translucent;
        _searchBar.opaque = navBar.opaque;
        _searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self];
        _searchDisplayController.delegate = self;
        _searchDisplayController.searchResultsDataSource = self;
        _searchDisplayController.searchResultsDelegate = self;
        _searchBar.delegate = self;
166
167
168
169

        UITapGestureRecognizer *tapTwiceGesture = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(tapTwiceGestureAction:)];
        [tapTwiceGesture setNumberOfTapsRequired:2];
        [self.navigationController.navigationBar addGestureRecognizer:tapTwiceGesture];
170
171
172
    }

    _searchData = [[NSMutableArray alloc] init];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
173
174
}

175
176
177
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
178
    [self.collectionView.collectionViewLayout invalidateLayout];
179
    [self _displayEmptyLibraryViewIfNeeded];
180
181
}

182
183
- (void)viewDidAppear:(BOOL)animated
{
184
185
    [super viewDidAppear:animated];

186
187
188
189
190
191
192
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if (![[defaults objectForKey:kDisplayedFirstSteps] boolValue]) {
        [self.emptyLibraryView performSelector:@selector(learnMore:) withObject:nil afterDelay:1.];
        [defaults setObject:[NSNumber numberWithBool:YES] forKey:kDisplayedFirstSteps];
        [defaults synchronize];
    }

193
194
195
    if ([[MLMediaLibrary sharedMediaLibrary] libraryNeedsUpgrade]) {
        self.navigationItem.rightBarButtonItem = nil;
        self.navigationItem.leftBarButtonItem = nil;
Gleb Pinigin's avatar
Gleb Pinigin committed
196
        self.title = @"";
197
198
199
        self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"UPGRADING_LIBRARY", @"");
        self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = YES;
        [self.emptyLibraryView.activityIndicator startAnimating];
Gleb Pinigin's avatar
Gleb Pinigin committed
200
        self.emptyLibraryView.frame = self.view.bounds;
201
202
203
204
205
206
207
        [self.view addSubview:self.emptyLibraryView];

        [[MLMediaLibrary sharedMediaLibrary] setDelegate: self];
        [[MLMediaLibrary sharedMediaLibrary] performSelectorInBackground:@selector(upgradeLibrary) withObject:nil];
        return;
    }

208
    if (_foundMedia.count < 1)
209
        [self updateViewContents];
210
211
212
213
214
215
216
217
218
    [[MLMediaLibrary sharedMediaLibrary] performSelector:@selector(libraryDidAppear) withObject:nil afterDelay:1.];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [[MLMediaLibrary sharedMediaLibrary] libraryDidDisappear];
}

219
220
221
222
223
224
225
226
227
228
229
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake)
        [[VLCBugreporter sharedInstance] handleBugreportRequest];
}

230
231
232
- (void)openMediaObject:(NSManagedObject *)mediaObject
{
    if ([mediaObject isKindOfClass:[MLAlbum class]]) {
233
        _foundMedia = [NSMutableArray arrayWithArray:[(MLAlbum *)mediaObject sortedTracks]];
234
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
235
236
237
238
        if (_libraryMode == VLCLibraryModeAllFiles)
            self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
        else
            [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_MUSIC", @"")];
239
240
241
        self.title = [(MLAlbum*)mediaObject name];
        [self reloadViews];
    } else if ([mediaObject isKindOfClass:[MLShow class]]) {
242
        _foundMedia = [NSMutableArray arrayWithArray:[(MLShow *)mediaObject sortedEpisodes]];
243
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
244
245
246
247
        if (_libraryMode == VLCLibraryModeAllFiles)
            self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
        else
            [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_SERIES", @"")];
248
249
        self.title = [(MLShow*)mediaObject name];
        [self reloadViews];
Carola's avatar
Carola committed
250
251
252
    } else if ([mediaObject isKindOfClass:[MLLabel class]]) {
        MLLabel *folder = (MLLabel*) mediaObject;
        inFolder = YES;
253
        if (!_usingTableViewToShowData) {
254
255
256
257
258
259
260
            if (![self.collectionView.collectionViewLayout isEqual:_reorderLayout]) {
                for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
                    if (recognizer == _folderLayout.panGestureRecognizer || recognizer == _folderLayout.longPressGestureRecognizer || recognizer == _longPressGestureRecognizer)
                        [self.collectionView removeGestureRecognizer:recognizer];
                }
                _reorderLayout = [[LXReorderableCollectionViewFlowLayout alloc] init];
                [self.collectionView setCollectionViewLayout:_reorderLayout animated:NO];
261
            }
Carola's avatar
Carola committed
262
        }
263
        _foundMedia = [NSMutableArray arrayWithArray:[folder sortedFolderItems]];
Carola's avatar
Carola committed
264
265
266
267
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
        self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
        self.title = [folder name];

268
        UIBarButtonItem *removeFromFolder = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:@selector(removeFromFolder)];
Carola's avatar
Carola committed
269
270
271
272
273
274
        NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
        toolbarItems[0] = removeFromFolder;
        self.toolbarItems = toolbarItems;

        [self reloadViews];
        return;
275
276
    } else
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaFromManagedObject:mediaObject];
277
278
}

279
- (void)removeMediaObject:(id)managedObject updateDatabase:(BOOL)updateDB
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
{
        // delete all tracks from an album
    if ([managedObject isKindOfClass:[MLAlbum class]]) {
        MLAlbum *album = managedObject;
        NSSet *iterAlbumTrack = [NSSet setWithSet:album.tracks];

        for (MLAlbumTrack *track in iterAlbumTrack) {
            NSSet *iterFiles = [NSSet setWithSet:track.files];

            for (MLFile *file in iterFiles)
                [self _deleteMediaObject:file];
        }
        // delete all episodes from a show
    } else if ([managedObject isKindOfClass:[MLShow class]]) {
        MLShow *show = managedObject;
        NSSet *iterShowEpisodes = [NSSet setWithSet:show.episodes];

        for (MLShowEpisode *episode in iterShowEpisodes) {
            NSSet *iterFiles = [NSSet setWithSet:episode.files];

            for (MLFile *file in iterFiles)
                [self _deleteMediaObject:file];
        }
        // delete all files from an episode
    } else if ([managedObject isKindOfClass:[MLShowEpisode class]]) {
        MLShowEpisode *episode = managedObject;
        NSSet *iterFiles = [NSSet setWithSet:episode.files];

        for (MLFile *file in iterFiles)
            [self _deleteMediaObject:file];
        // delete all files from a track
    } else if ([managedObject isKindOfClass:[MLAlbumTrack class]]) {
        MLAlbumTrack *track = managedObject;
        NSSet *iterFiles = [NSSet setWithSet:track.files];

        for (MLFile *file in iterFiles)
            [self _deleteMediaObject:file];
Carola's avatar
Carola committed
317
318
319
320
321
322
323
324
325
    } else if ([managedObject isKindOfClass:[MLLabel class]]) {
        MLLabel *folder = managedObject;
        NSSet *iterFiles = [NSSet setWithSet:folder.files];
        [folder removeFiles:folder.files];
        for (MLFile *file in iterFiles)
            [self _deleteMediaObject:file];
        [[MLMediaLibrary sharedMediaLibrary] removeObject:folder];
    }
    else
326
        [self _deleteMediaObject:managedObject];
327

328
329
330
331
    if (updateDB) {
        [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
        [self updateViewContents];
    }
332
333
334
}

- (void)_deleteMediaObject:(MLFile *)mediaObject
335
{
336
337
338
    if (inFolder)
        [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];

339
340
341
342
343
344
345
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *folderLocation = [[[NSURL URLWithString:mediaObject.url] path] stringByDeletingLastPathComponent];
    NSArray *allfiles = [fileManager contentsOfDirectoryAtPath:folderLocation error:nil];
    NSString *fileName = [[[[NSURL URLWithString:mediaObject.url] path] lastPathComponent] stringByDeletingPathExtension];
    NSIndexSet *indexSet = [allfiles indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
       return ([obj rangeOfString:fileName].location != NSNotFound);
    }];
346
    NSUInteger count = indexSet.count;
347
348
349
350
351
352
353
354
355
    NSString *additionalFilePath;
    NSUInteger currentIndex = [indexSet firstIndex];
    for (unsigned int x = 0; x < count; x++) {
        additionalFilePath = allfiles[currentIndex];
        if ([additionalFilePath isSupportedSubtitleFormat])
            [fileManager removeItemAtPath:[folderLocation stringByAppendingPathComponent:additionalFilePath] error:nil];
        currentIndex = [indexSet indexGreaterThanIndex:currentIndex];
    }
    [fileManager removeItemAtPath:[[NSURL URLWithString:mediaObject.url] path] error:nil];
356
357
358
359
}

- (void)_displayEmptyLibraryViewIfNeeded
{
360
361
362
    if (self.emptyLibraryView.superview)
        [self.emptyLibraryView removeFromSuperview];

Gleb Pinigin's avatar
Gleb Pinigin committed
363
    if (_foundMedia.count == 0) {
Carola's avatar
Carola committed
364
365
366
        self.emptyLibraryView.emptyLibraryLabel.text = inFolder ? NSLocalizedString(@"FOLDER_EMPTY", @"") : NSLocalizedString(@"EMPTY_LIBRARY", @"");
        self.emptyLibraryView.emptyLibraryLongDescriptionLabel.text = inFolder ? NSLocalizedString(@"FOLDER_EMPTY_LONG", @"") : NSLocalizedString(@"EMPTY_LIBRARY_LONG", @"");
        self.emptyLibraryView.learnMoreButton.hidden = inFolder;
Gleb Pinigin's avatar
Gleb Pinigin committed
367
        self.emptyLibraryView.frame = self.view.bounds;
368
        [self.view addSubview:self.emptyLibraryView];
369
        self.navigationItem.rightBarButtonItem = nil;
370
371
    } else
        self.navigationItem.rightBarButtonItem = self.editButtonItem;
372

373
    if (_usingTableViewToShowData)
374
375
        _tableView.separatorStyle = (_foundMedia.count > 0)? UITableViewCellSeparatorStyleSingleLine:
                                                             UITableViewCellSeparatorStyleNone;
376
    else
377
        [self.collectionView.collectionViewLayout invalidateLayout];
378
379
}

380
381
- (void)libraryUpgradeComplete
{
Gleb Pinigin's avatar
Gleb Pinigin committed
382
383
    self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
    self.navigationItem.leftBarButtonItem = _menuButton;
384
385
386
387
388
    self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = NO;
    self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", @"");
    [self.emptyLibraryView.activityIndicator stopAnimating];
    [self.emptyLibraryView removeFromSuperview];

389
390
391
392
393
394
    [self updateViewContents];
}

- (void)libraryWasUpdated
{
    [self updateViewContents];
395
396
}

397
- (void)updateViewContents
Felix Paul Kühne's avatar
Felix Paul Kühne committed
398
{
399
400
    _foundMedia = [[NSMutableArray alloc] init];

401
402
403
404
405
406
407
408
    self.navigationItem.leftBarButtonItem = _menuButton;
    if (_libraryMode == VLCLibraryModeAllAlbums)
        self.title = NSLocalizedString(@"LIBRARY_MUSIC", @"");
    else if( _libraryMode == VLCLibraryModeAllSeries)
        self.title = NSLocalizedString(@"LIBRARY_SERIES", @"");
    else
        self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");

409
410
    /* add all albums */
    if (_libraryMode != VLCLibraryModeAllSeries) {
411
        NSArray *rawAlbums = [MLAlbum allAlbums];
412
        for (MLAlbum *album in rawAlbums) {
Carola's avatar
Carola committed
413
            if (album.name.length > 0 && album.tracks.count > 1)
414
415
                [_foundMedia addObject:album];
        }
416
417
418
419
420
    }
    if (_libraryMode == VLCLibraryModeAllAlbums) {
        [self reloadViews];
        return;
    }
421

422
423
424
    /* add all shows */
    NSArray *rawShows = [MLShow allShows];
    for (MLShow *show in rawShows) {
Carola's avatar
Carola committed
425
        if (show.name.length > 0 && show.episodes.count > 1)
426
427
428
429
430
431
432
            [_foundMedia addObject:show];
    }
    if (_libraryMode == VLCLibraryModeAllSeries) {
        [self reloadViews];
        return;
    }

Carola's avatar
Carola committed
433
434
435
436
437
    /* add all folders*/
    NSArray *allFolders = [MLLabel allLabels];
    for (MLLabel *folder in allFolders)
        [_foundMedia addObject:folder];

438
439
440
    /* add all remaining files */
    NSArray *allFiles = [MLFile allFiles];
    for (MLFile *file in allFiles) {
Carola's avatar
Carola committed
441
442
        if (file.labels.count > 0) continue;

443
444
        if (!file.isShowEpisode && !file.isAlbumTrack)
            [_foundMedia addObject:file];
445
446
447
        else if (file.isShowEpisode) {
            if (file.showEpisode.show.episodes.count < 2)
                [_foundMedia addObject:file];
448
449
450
451
452
453
454
455
456

            /* older MediaLibraryKit versions don't send a show name in a popular
             * corner case. hence, we need to work-around here and force a reload
             * afterwards as this could lead to the 'all my shows are gone' 
             * syndrome (see #10435, #10464, #10432 et al) */
            if (file.showEpisode.show.name.length == 0) {
                file.showEpisode.show.name = NSLocalizedString(@"UNTITLED_SHOW", @"");
                [self performSelector:@selector(updateViewContents) withObject:nil afterDelay:0.1];
            }
457
458
459
460
        } else if (file.isAlbumTrack) {
            if (file.albumTrack.album.tracks.count < 2)
                [_foundMedia addObject:file];
        }
461
    }
462

463
    [self reloadViews];
464
}
465

466
- (void)reloadViews
467
{
468
    if (_usingTableViewToShowData)
469
        [self.tableView reloadData];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
470
471
    else
        [self.collectionView reloadData];
472
473

    [self _displayEmptyLibraryViewIfNeeded];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
474
475
}

476
477
#pragma mark - Table View

Felix Paul Kühne's avatar
Felix Paul Kühne committed
478
479
480
481
482
483
484
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
485
486
487
    if (tableView == self.searchDisplayController.searchResultsTableView)
        return _searchData.count;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
488
489
490
491
492
493
    return _foundMedia.count;
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
494
    static NSString *CellIdentifier = @"PlaylistCell";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
495

496
    VLCPlaylistTableViewCell *cell = (VLCPlaylistTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
497
    if (cell == nil)
498
        cell = [VLCPlaylistTableViewCell cellWithReuseIdentifier:CellIdentifier];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
499

500
501
502
503
    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightGestureAction:)];
    [swipeRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
    [cell addGestureRecognizer:swipeRight];

504
    NSInteger row = indexPath.row;
505
506
507
508
509

    if (tableView == self.searchDisplayController.searchResultsTableView)
        cell.mediaObject = _searchData[row];
    else
        cell.mediaObject = _foundMedia[row];
510

Felix Paul Kühne's avatar
Felix Paul Kühne committed
511
512
513
    return cell;
}

514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    MLFile* object = [_foundMedia objectAtIndex:fromIndexPath.item];
    [_foundMedia removeObjectAtIndex:fromIndexPath.item];
    [_foundMedia insertObject:object atIndex:toIndexPath.item];
    object.folderTrackNumber = @(toIndexPath.item - 1);
    object = [_foundMedia objectAtIndex:fromIndexPath.item];
    object.folderTrackNumber = @(fromIndexPath.item - 1);
}

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return inFolder;
}

529
530
531
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor colorWithWhite:.122 alpha:1.];
532
    cell.multipleSelectionBackgroundView.backgroundColor = cell.backgroundColor;
533
534
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
535
536
537
538
539
540
541
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
542
    if (editingStyle == UITableViewCellEditingStyleDelete)
543
        [self removeMediaObject: _foundMedia[indexPath.row] updateDatabase:YES];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
544
545
}

546
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Felix Paul Kühne's avatar
Felix Paul Kühne committed
547
{
Carola's avatar
Carola committed
548
549
550
551
552
553
554
    if (tableView.isEditing) {
        if (_libraryMode == VLCLibraryModeCreateFolder) {
            _folderObject = _foundMedia[indexPath.row];
            _libraryMode = _previousLibraryMode;
            [self updateViewContents];
            [self createFolderWithName:nil];
        }
555
        return;
Carola's avatar
Carola committed
556
    }
557

558
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
559
560
561
562
563
564
    NSManagedObject *selectedObject;
    if (tableView == self.searchDisplayController.searchResultsTableView)
        selectedObject = _searchData[indexPath.row];
    else
        selectedObject = _foundMedia[indexPath.row];

565
    if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
566
567
568
569
        _tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
        NSUInteger count = _tracks.count;
        _list = [[VLCMediaList alloc] init];
        _list.delegate = self;
570
        MLFile *file;
571
        VLCMedia *media;
572
        for (NSInteger x = count - 1; x > -1; x--) {
573
            file = [(MLAlbumTrack*)_tracks[x] files].anyObject;
574
575
            media = [VLCMedia mediaWithURL: [NSURL URLWithString:file.url]];
            [media parse];
576
            [_list addMedia:media];
577
        }
578
        _mediaToPlayIndex = indexPath.row;
Carola's avatar
Carola committed
579
580
    } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
        MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
581
582
583
584
        _tracks = [folder sortedFolderItems];
        NSUInteger count = _tracks.count;
        _list = [[VLCMediaList alloc] init];
        _list.delegate = self;
Carola's avatar
Carola committed
585
586
        MLFile *file;
        for (NSInteger x = count - 1; x > -1; x--) {
587
588
            file = (MLFile *)_tracks[x];
            [_list addMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:file.url]]];
Carola's avatar
Carola committed
589
        }
590
        _mediaToPlayIndex = indexPath.row;
591
592
593
594
595
    }

    if (_searchDisplayController.active == YES)
        [_searchDisplayController setActive:NO animated:YES];
    [self openMediaObject:selectedObject];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
596
597
}

598
599
600
601
602
603
604
605
606
#pragma mark - VLCMedialistDelegate
- (void)mediaList:(VLCMediaList *)aMediaList mediaAdded:(VLCMedia *)media atIndex:(NSInteger)index
{
    if (index == _tracks.count - 1) {
        NSManagedObject *selectedObject = _foundMedia[_mediaToPlayIndex];
        [(VLCAppDelegate *)[UIApplication sharedApplication].delegate openMediaList:_list atIndex:(int)[_tracks indexOfObject:selectedObject]];
    }
}

607
#pragma mark - Gesture Action
608
609
610
611
612
613
614
615
616
617
618
619
620
621
- (void)swipeRightGestureAction:(UIGestureRecognizer *)recognizer
{
    if ([[self.editButtonItem title] isEqualToString:NSLocalizedString(@"BUTTON_CANCEL",@"")])
        [self setEditing:NO animated:YES];
    else {
        [self setEditing:YES animated:YES];

        NSIndexPath *path = [(UITableView *)self.view indexPathForRowAtPoint:[recognizer locationInView:self.view]];
        [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:path.row inSection:path.section]
                                    animated:YES
                              scrollPosition:UITableViewScrollPositionNone];
    }
}

622
623
624
625
626
627
628
629
630
- (void)tapTwiceGestureAction:(UIGestureRecognizer *)recognizer
{
    _searchBar.hidden = !_searchBar.hidden;
    if (_searchBar.hidden)
        self.tableView.tableHeaderView = nil;
    else
        self.tableView.tableHeaderView = _searchBar;
}

631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
#pragma mark - Collection View
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _foundMedia.count;
}

- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    VLCPlaylistCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PlaylistCell" forIndexPath:indexPath];
    cell.mediaObject = _foundMedia[indexPath.row];
    cell.collectionView = _collectionView;

    [cell setEditing:self.editing animated:NO];

    return cell;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
650
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
651
        if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
652
653
654
655
            return CGSizeMake(341., 190.);
        else
            return CGSizeMake(384., 216.);
    }
656

657
658
659
660
661
    return CGSizeMake(298.0, 220.0);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
662
    if (SYSTEM_RUNS_IOS7_OR_LATER)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
663
        return UIEdgeInsetsZero;
664
665
666
    return UIEdgeInsetsMake(0.0, 34.0, 0.0, 34.0);
}

667
668
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
669
670
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return 0.;
671
672
673
674
675
    return 10.0;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
676
677
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return 0.;
678
679
680
    return 10.0;
}

681
682
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
683
    if (self.editing) {
Carola's avatar
Carola committed
684
685
686
        if (_libraryMode == VLCLibraryModeCreateFolder) {
            _folderObject = _foundMedia[indexPath.item];
            [self createFolderWithName:nil];
687
             _libraryMode = _previousLibraryMode;
Carola's avatar
Carola committed
688
        }
689
690
691
692
        [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
        return;
    }

693
    NSManagedObject *selectedObject = _foundMedia[indexPath.row];
694
695
696
697
698
699
700
    if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
        VLCMediaList *list;
        NSArray *tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
        NSUInteger count = tracks.count;
        list = [[VLCMediaList alloc] init];

        MLFile *file;
701
        for (NSInteger x = count - 1; x > -1; x--) {
702
703
704
            file = [(MLAlbumTrack*)tracks[x] files].anyObject;
            [list addMedia:[VLCMedia mediaWithURL: [NSURL URLWithString:file.url]]];
        }
705
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[tracks indexOfObject:selectedObject]];
Carola's avatar
Carola committed
706
707
708
    } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
        VLCMediaList *list;
        MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
Carola's avatar
Carola committed
709
        NSArray *folderTracks = [folder sortedFolderItems];
Carola's avatar
Carola committed
710
711
712
713
714
715
716
717
718
        NSUInteger count = folderTracks.count;
        list = [[VLCMediaList alloc] init];

        MLFile *file;
        for (NSInteger x = count - 1; x > -1; x--) {
            file = (MLFile *)folderTracks[x];
            [list addMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:file.url]]];
        }
        [(VLCAppDelegate *)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[folderTracks indexOfObject:selectedObject]];
719
720
    } else
        [self openMediaObject:selectedObject];
721
722
}

723
724
725
726
727
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
}

Carola's avatar
Carola committed
728
729
730
- (void)collectionView:(UICollectionView *)collectionView removeItemFromFolderAtIndexPathIfNeeded:(NSIndexPath *)indexPath
{
    MLFile *mediaObject = (MLFile *)_foundMedia[indexPath.item];
731
    [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
Carola's avatar
Carola committed
732
    mediaObject.labels = nil;
733
    mediaObject.folderTrackNumber = nil;
Carola's avatar
Carola committed
734
735
736
737

    [self backToAllItems:nil];
}

738
739
740
741
742
743
744
745
746
747
- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath
{
    MLFile* object = [_foundMedia objectAtIndex:fromIndexPath.item];
    [_foundMedia removeObjectAtIndex:fromIndexPath.item];
    [_foundMedia insertObject:object atIndex:toIndexPath.item];
    object.folderTrackNumber = @(toIndexPath.item - 1);
    object = [_foundMedia objectAtIndex:fromIndexPath.item];
    object.folderTrackNumber = @(fromIndexPath.item - 1);
}

Carola's avatar
Carola committed
748
749
- (void)collectionView:(UICollectionView *)collectionView requestToMoveItemAtIndexPath:(NSIndexPath *)itemPath intoFolderAtIndexPath:(NSIndexPath *)folderPath
{
750
    BOOL validFileTypeAtFolderPath = ([_foundMedia[folderPath.item] isKindOfClass:[MLFile class]] || [_foundMedia[folderPath.item] isKindOfClass:[MLLabel class]]) && [_foundMedia[itemPath.item] isKindOfClass:[MLFile class]];
Carola's avatar
Carola committed
751

752
    if (!validFileTypeAtFolderPath) {
Carola's avatar
Carola committed
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
        VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_INVALID_TYPE_TITLE", @"") message:NSLocalizedString(@"FOLDER_INVALID_TYPE_MESSAGE", @"") cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", @"")]];

        alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
            [self updateViewContents];
        };
        [alert show];
        return;
    }

    BOOL isFolder = [_foundMedia[folderPath.item] isKindOfClass:[MLLabel class]];

    if (isFolder){
        MLLabel *folder = _foundMedia[folderPath.item];
        MLFile *file = _foundMedia[itemPath.item];
        [file setLabels:[[NSSet alloc] initWithObjects:folder, nil]];
768
        file.folderTrackNumber = @([folder.files count] - 1);
Carola's avatar
Carola committed
769
770
771
772
773
774
775
776
777
778
779
        [_foundMedia removeObjectAtIndex:itemPath.item];
        [self updateViewContents];
    } else {
        _folderObject = _foundMedia[folderPath.item];
        _indexPaths = [NSMutableArray arrayWithArray:@[itemPath]];
        [self showCreateFolderAlert];
    }
}

#pragma mark - Folder implementation

780
781
782
783
784
785
786
787
788
789
790
791
- (void)rearrangeFolderTrackNumbersForRemovedItem:(MLFile *) mediaObject
{
    MLLabel *label = [mediaObject.labels anyObject];
    NSSet *allFiles = label.files;
    for (MLFile *file in allFiles) {
        if (file.folderTrackNumber > mediaObject.folderTrackNumber) {
            int value = [file.folderTrackNumber intValue];
            file.folderTrackNumber = [NSNumber numberWithInt:value - 1];
        }
    }
}

Carola's avatar
Carola committed
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
- (void)showCreateFolderAlert
{
    VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_CHOOSE_NAME_TITLE", @"") message:NSLocalizedString(@"FOLDER_CHOOSE_NAME_MESSAGE", @"") cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", @"") otherButtonTitles:@[NSLocalizedString(@"BUTTON_SAVE", @"")]];
    [alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
    [[alert textFieldAtIndex:0] setText:NSLocalizedString(@"FOLDER_NAME_PLACEHOLDER", @"")];
    [[alert textFieldAtIndex:0] setClearButtonMode:UITextFieldViewModeAlways];

    __weak VLCAlertView *weakAlert = alert;
    alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
        if (cancelled)
            [self updateViewContents];
        else
            [self createFolderWithName:[weakAlert textFieldAtIndex:0].text];
    };
    [alert show];
}

- (void)createFolder
{
    if (_libraryMode == VLCLibraryModeCreateFolder) {
        _libraryMode = _previousLibraryMode;
        [self updateViewContents];
        [self showCreateFolderAlert];
        return;
    }
817
    if (!_usingTableViewToShowData)
Carola's avatar
Carola committed
818
819
820
821
        _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
    else
        _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];

822
823
    for (NSInteger i = _indexPaths.count - 1; i >=0; i--) {
        NSIndexPath *path = _indexPaths[i];
Carola's avatar
Carola committed
824
        id mediaObject;
825
        if (!_usingTableViewToShowData)
Carola's avatar
Carola committed
826
827
828
829
            mediaObject = _foundMedia[path.item];
        else
            mediaObject = _foundMedia[path.row];
        if ([mediaObject isKindOfClass:[MLLabel class]])
830
            [_indexPaths removeObject:path];
Carola's avatar
Carola committed
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
    }

    if ([_indexPaths count] != 0) {
        NSArray *folder = [MLLabel allLabels];
        //if we already have folders display them
        if ([folder count] > 0) {
            _foundMedia = [NSMutableArray arrayWithArray:folder];
            self.title = NSLocalizedString(@"SELECT_FOLDER", @"");
            _previousLibraryMode = _libraryMode;
            _libraryMode = VLCLibraryModeCreateFolder;
            [self reloadViews];
            return;
        }
    }
    //no selected items or no existing folder ask for foldername
    [self showCreateFolderAlert];
}

- (void)removeFromFolder
{
851
    if (!_usingTableViewToShowData)
Carola's avatar
Carola committed
852
853
854
855
856
857
        _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
    else
        _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];

    [_indexPaths sortUsingSelector:@selector(compare:)];

858
    for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
859
        NSIndexPath *path = _indexPaths[i];
860
        MLFile *file = (MLFile *)_foundMedia[_usingTableViewToShowData ? path.row : path.item];
Carola's avatar
Carola committed
861
862

        MLLabel *folder = [file.labels anyObject];
863
        [self rearrangeFolderTrackNumbersForRemovedItem:file];
Carola's avatar
Carola committed
864
        file.labels = nil;
865
        file.folderTrackNumber = nil;
Carola's avatar
Carola committed
866
867
868
869
        [_foundMedia removeObject:file];

        if ([folder.files count] == 0) {
            [self removeMediaObject:folder updateDatabase:YES];
870
            [self setEditing:NO];
Carola's avatar
Carola committed
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
            [self backToAllItems:nil];
        }
    }
    [self reloadViews];
}

- (void)createFolderWithName:(NSString *)folderName
{
    NSArray *labels = [MLLabel allLabels];
    for (MLLabel *label in labels){
        if ([label.name isEqualToString:folderName]) {
            _folderObject = nil;
            _indexPaths = nil;
            VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_TITLE", @"") message:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_MESSAGE", @"") cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", @"")]];

            alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
                [self updateViewContents];
            };
            [alert show];
            return;
        }
    }

    if (_folderObject != nil) {
895
        NSUInteger folderIndex = [_foundMedia indexOfObject:_folderObject];
Carola's avatar
Carola committed
896
897
898
899
900
        //item got dragged onto item
        if ([_foundMedia[folderIndex] isKindOfClass:[MLFile class]]) {
            MLFile *file = _foundMedia[folderIndex];
            MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
            label.name = folderName;
901

Carola's avatar
Carola committed
902
            file.labels = [NSSet setWithObjects:label,nil];
903
            NSNumber *folderTrackNumber = [NSNumber numberWithInt:(int)[label files].count - 1];
904
            file.folderTrackNumber = folderTrackNumber;
Carola's avatar
Carola committed
905
906
907

            [_foundMedia removeObjectAtIndex:folderIndex];
            [_foundMedia insertObject:label atIndex:folderIndex];
908
909
910
            MLFile *itemFile = _foundMedia[((NSIndexPath *)_indexPaths[0]).item];
            itemFile.labels = file.labels;
            [_foundMedia removeObjectAtIndex:((NSIndexPath *)_indexPaths[0]).item];
911
            itemFile.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
912
913
914
915
916
        } else {
            //item got dragged onto folder or items should be added to folder
            MLLabel *label = _foundMedia[folderIndex];
            [_indexPaths sortUsingSelector:@selector(compare:)];

917
            for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
918
                NSIndexPath *path = _indexPaths[i];
919
                if (_libraryMode != VLCLibraryModeCreateFolder && ![_foundMedia[path.row] isKindOfClass:[MLFile class]])
920
                    continue;
921
922
923
                if (_libraryMode == VLCLibraryModeCreateFolder)
                    [self updateViewContents];

924
925
926
                MLFile *file = _foundMedia[path.row];
                file.labels = [NSSet setWithObjects:label, nil];
                [_foundMedia removeObjectAtIndex:path.row];
927
                file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
928
929
930
931
932
933
934
935
936
937
938
939
            }
        }
        _folderObject = nil;
    } else {
        //create new folder
        MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
        label.name = folderName;

        //if items were selected
        if ([_indexPaths count] != 0) {
            [_indexPaths sortUsingSelector:@selector(compare:)];

940
            for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
941
                NSIndexPath *path = _indexPaths[i];
942
                if (!_usingTableViewToShowData) {
Carola's avatar
Carola committed
943
944
                    MLFile *file = _foundMedia[path.item];
                    file.labels = [NSSet setWithObjects:label, nil];
945
                    file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
946
947
948
949
                    [_foundMedia removeObjectAtIndex:path.item];
                } else {
                    MLFile *file = _foundMedia[path.row];
                    file.labels = [NSSet setWithObjects:label, nil];
950
                    file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
                    [_foundMedia removeObjectAtIndex:path.row];
                }
            }
        }
    }
    _indexPaths = nil;
    [self setEditing:NO];
    [self updateViewContents];
}

- (void)_collectionViewHandleLongPressGesture:(UIGestureRecognizer *) sender
{
    [self setEditing:YES animated:YES];
}

966
#pragma mark - UI implementation
967
968
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
969
    [super setEditing:editing animated:animated];
970

Felix Paul Kühne's avatar
Felix Paul Kühne committed
971
    UIBarButtonItem *editButton = self.editButtonItem;
972
973
    NSString *editImage = editing? @"doneButton": @"button";
    NSString *editImageHighlight = editing? @"doneButtonHighlight": @"buttonHighlight";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
974
    if (SYSTEM_RUNS_IOS7_OR_LATER)
975
976
977
978
979
980
981
982
        editButton.tintColor = [UIColor whiteColor];
    else {
        [editButton setBackgroundImage:[UIImage imageNamed:editImage] forState:UIControlStateNormal
                                     barMetrics:UIBarMetricsDefault];
        [editButton setBackgroundImage:[UIImage imageNamed:editImageHighlight]
                                       forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
        [editButton setTitleTextAttributes: editing ? @{UITextAttributeTextShadowColor : [UIColor whiteColor], UITextAttributeTextColor : [UIColor blackColor]} : @{UITextAttributeTextShadowColor : [UIColor colorWithWhite:0. alpha:.37], UITextAttributeTextColor : [UIColor whiteColor]} forState:UIControlStateNormal];
    }
Felix Paul Kühne's avatar
Felix Paul Kühne committed
983

984
    if (!_usingTableViewToShowData) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
985
        NSArray *visibleCells = self.collectionView.visibleCells;
986

Felix Paul Kühne's avatar
Felix Paul Kühne committed
987
988
        [visibleCells enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            VLCPlaylistCollectionViewCell *aCell = (VLCPlaylistCollectionViewCell*)obj;
989

Felix Paul Kühne's avatar
Felix Paul Kühne committed
990
991
            [aCell setEditing:editing animated:animated];
        }];
992
993
994
995
996
        self.collectionView.allowsMultipleSelection = editing;

        /* UIKit doesn't clear the selection automagically if we leave the editing mode
         * so we need to do so manually */
        if (!editing) {
997
            [self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
998
999
1000
1001
1002
            NSArray *selectedItems = [self.collectionView indexPathsForSelectedItems];
            NSUInteger count = selectedItems.count;

            for (NSUInteger x = 0; x < count; x++)
                [self.collectionView deselectItemAtIndexPath:selectedItems[x] animated:NO];
1003
1004
        } else
            [self.collectionView removeGestureRecognizer:_longPressGestureRecognizer];
1005
1006
    } else {
        self.tableView.allowsMultipleSelectionDuringEditing = editing;
1007
        [self.tableView setEditing:editing animated:YES];
1008
1009
        [self.editButtonItem setTitle:editing ? NSLocalizedString(@"BUTTON_CANCEL",@"") : NSLocalizedString(@"BUTTON_EDIT", @"")];
    }
Carola's avatar
Carola committed
1010
1011
1012
1013
1014
1015
1016

    if (_libraryMode == VLCLibraryModeCreateFolder) {
        _libraryMode = _previousLibraryMode;
        _indexPaths = nil;
        [self updateViewContents];
    }

1017
    self.navigationController.toolbarHidden = !editing;
1018
1019
}

1020
1021
1022
1023
1024
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleDelete;
}

1025
- (IBAction)leftButtonAction:(id)sender
1026
{
1027
    [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController] toggleSidebar:![(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController].sidebarShowing duration:kGHRevealSidebarDefaultAnimationDuration];
1028
1029
1030

    if (self.isEditing)
        [self setEditing:NO animated:YES];
1031
1032
}

1033
1034
- (IBAction)backToAllItems:(id)sender
{
1035
    if (!_usingTableViewToShowData) {
1036
1037
1038
1039
1040
1041
1042
1043
1044
        if (![self.collectionView.collectionViewLayout isEqual:_folderLayout]) {
            //for some reason the Gesturerecognizer block themselves if not removed manually
            for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
                if (recognizer == _reorderLayout.panGestureRecognizer || recognizer == _reorderLayout.longPressGestureRecognizer)
                    [self.collectionView removeGestureRecognizer:recognizer];
            }
            _folderLayout = [[VLCFolderCollectionViewFlowLayout alloc] init];
            [self.collectionView setCollectionViewLayout:_folderLayout animated:NO];
            [_collectionView addGestureRecognizer:_longPressGestureRecognizer];
1045
        }
Carola's avatar
Carola committed
1046
1047
1048
1049
1050
1051
    }
    inFolder = NO;
    UIBarButtonItem *createFolderItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
    NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
    toolbarItems[0] = createFolderItem;
    self.toolbarItems = toolbarItems;
1052
    [self setLibraryMode:_libraryMode];
1053
    [self updateViewContents];
1054
1055
}

1056
- (void)_endEditingWithHardReset:(BOOL)hardReset
1057
{
1058
    [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
1059
1060
1061
1062
    if (hardReset)
        [self updateViewContents];
    else
        [self reloadViews];