VLCPlaylistViewController.m 47.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

Carola's avatar
Carola committed
46
@interface VLCPlaylistViewController () <VLCFolderCollectionViewDelegateFlowLayout, LXReorderableCollectionViewDataSource, LXReorderableCollectionViewDelegateFlowLayout, UITableViewDataSource, UITableViewDelegate, MLMediaLibrary> {
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;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
57
}
58
59

@property (nonatomic, strong) UITableView *tableView;
60
@property (nonatomic, strong) UICollectionView *collectionView;
61
62
@property (nonatomic, strong) EmptyLibraryView *emptyLibraryView;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
63
64
65
66
@end

@implementation VLCPlaylistViewController

67
68
69
70
71
72
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults registerDefaults:@{kDisplayedFirstSteps : [NSNumber numberWithBool:NO]}];
}

73
74
- (void)loadView
{
75
76
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
77
        _tableView.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
78
79
        _tableView.rowHeight = [VLCPlaylistTableViewCell heightOfCell];
        _tableView.separatorColor = [UIColor colorWithWhite:.122 alpha:1.];
80
81
        _tableView.delegate = self;
        _tableView.dataSource = self;
82
        _tableView.opaque = YES;
83
84
        self.view = _tableView;
    } else {
Carola's avatar
Carola committed
85
86
        _folderLayout = [[VLCFolderCollectionViewFlowLayout alloc] init];
        _collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:_folderLayout];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
87
88
89
90
        _collectionView.alwaysBounceVertical = YES;
        _collectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
91
92
        _collectionView.opaque = YES;
        _collectionView.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
93
        self.view = _collectionView;
94
95
        _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_collectionViewHandleLongPressGesture:)];
        [_collectionView addGestureRecognizer:_longPressGestureRecognizer];
96
        if (SYSTEM_RUNS_IOS7_OR_LATER)
97
            [_collectionView registerNib:[UINib nibWithNibName:@"VLCFuturePlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
98
        else
99
            [_collectionView registerNib:[UINib nibWithNibName:@"VLCPlaylistCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"PlaylistCell"];
100
        self.view.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
101
102
    }

103
    _libraryMode = VLCLibraryModeAllFiles;
104

105
106
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.emptyLibraryView = [[[NSBundle mainBundle] loadNibNamed:@"VLCEmptyLibraryView" owner:self options:nil] lastObject];
107
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
108
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.numberOfLines = 0;
109
110
111
112
}

#pragma mark -

Felix Paul Kühne's avatar
Felix Paul Kühne committed
113
114
115
- (void)viewDidLoad
{
    [super viewDidLoad];
116
    self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
117
118
    _menuButton = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(leftButtonAction:)];
    self.navigationItem.leftBarButtonItem = _menuButton;
119

Felix Paul Kühne's avatar
Felix Paul Kühne committed
120
    if (SYSTEM_RUNS_IOS7_OR_LATER)
121
122
123
124
125
126
127
        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
128

129
130
131
    _emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", @"");
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.text = NSLocalizedString(@"EMPTY_LIBRARY_LONG", @"");
    [_emptyLibraryView.emptyLibraryLongDescriptionLabel sizeToFit];
132
    [_emptyLibraryView.learnMoreButton setTitle:NSLocalizedString(@"BUTTON_LEARN_MORE", @"") forState:UIControlStateNormal];
Carola's avatar
Carola committed
133
134
    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
135
    self.navigationController.toolbar.barStyle = UIBarStyleBlack;
136

137
138
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
        self.navigationController.toolbar.tintColor = [UIColor whiteColor];
139
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
140
141
    } else
        [self.navigationController.toolbar setBackgroundImage:[UIImage imageNamed:@"bottomBlackBar"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
142
143
}

144
145
146
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
147
    [self.collectionView.collectionViewLayout invalidateLayout];
148
    [self _displayEmptyLibraryViewIfNeeded];
149
150
}

151
152
- (void)viewDidAppear:(BOOL)animated
{
153
154
    [super viewDidAppear:animated];

155
156
157
158
159
160
161
    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];
    }

162
163
164
    if ([[MLMediaLibrary sharedMediaLibrary] libraryNeedsUpgrade]) {
        self.navigationItem.rightBarButtonItem = nil;
        self.navigationItem.leftBarButtonItem = nil;
Gleb Pinigin's avatar
Gleb Pinigin committed
165
        self.title = @"";
166
167
168
        self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"UPGRADING_LIBRARY", @"");
        self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = YES;
        [self.emptyLibraryView.activityIndicator startAnimating];
Gleb Pinigin's avatar
Gleb Pinigin committed
169
        self.emptyLibraryView.frame = self.view.bounds;
170
171
172
173
174
175
176
        [self.view addSubview:self.emptyLibraryView];

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

177
    if (_foundMedia.count < 1)
178
        [self updateViewContents];
179
180
181
182
183
184
185
186
187
    [[MLMediaLibrary sharedMediaLibrary] performSelector:@selector(libraryDidAppear) withObject:nil afterDelay:1.];
}

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

188
189
190
191
192
193
194
195
196
197
198
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

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

199
200
201
- (void)openMediaObject:(NSManagedObject *)mediaObject
{
    if ([mediaObject isKindOfClass:[MLAlbum class]]) {
202
        _foundMedia = [NSMutableArray arrayWithArray:[(MLAlbum *)mediaObject sortedTracks]];
203
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
204
205
206
207
        if (_libraryMode == VLCLibraryModeAllFiles)
            self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
        else
            [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_MUSIC", @"")];
208
209
210
        self.title = [(MLAlbum*)mediaObject name];
        [self reloadViews];
    } else if ([mediaObject isKindOfClass:[MLShow class]]) {
211
        _foundMedia = [NSMutableArray arrayWithArray:[(MLShow *)mediaObject sortedEpisodes]];
212
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
213
214
215
216
        if (_libraryMode == VLCLibraryModeAllFiles)
            self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
        else
            [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_SERIES", @"")];
217
218
        self.title = [(MLShow*)mediaObject name];
        [self reloadViews];
Carola's avatar
Carola committed
219
220
221
222
    } else if ([mediaObject isKindOfClass:[MLLabel class]]) {
        MLLabel *folder = (MLLabel*) mediaObject;
        inFolder = YES;
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
223
224
225
226
227
228
229
            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];
230
            }
Carola's avatar
Carola committed
231
        }
232
        _foundMedia = [NSMutableArray arrayWithArray:[folder sortedFolderItems]];
Carola's avatar
Carola committed
233
234
235
236
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
        self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", @"");
        self.title = [folder name];

237
        UIBarButtonItem *removeFromFolder = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:@selector(removeFromFolder)];
Carola's avatar
Carola committed
238
239
240
241
242
243
        NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
        toolbarItems[0] = removeFromFolder;
        self.toolbarItems = toolbarItems;

        [self reloadViews];
        return;
244
245
    } else
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaFromManagedObject:mediaObject];
246
247
}

248
- (void)removeMediaObject:(id)managedObject updateDatabase:(BOOL)updateDB
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
{
        // 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
286
287
288
289
290
291
292
293
294
    } 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
295
        [self _deleteMediaObject:managedObject];
296

297
298
299
300
    if (updateDB) {
        [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
        [self updateViewContents];
    }
301
302
303
}

- (void)_deleteMediaObject:(MLFile *)mediaObject
304
{
305
306
307
    if (inFolder)
        [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];

308
309
310
311
312
313
314
    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);
    }];
315
    NSUInteger count = indexSet.count;
316
317
318
319
320
321
322
323
324
    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];
325
326
327
328
}

- (void)_displayEmptyLibraryViewIfNeeded
{
329
330
331
    if (self.emptyLibraryView.superview)
        [self.emptyLibraryView removeFromSuperview];

Gleb Pinigin's avatar
Gleb Pinigin committed
332
    if (_foundMedia.count == 0) {
Carola's avatar
Carola committed
333
334
335
        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
336
        self.emptyLibraryView.frame = self.view.bounds;
337
        [self.view addSubview:self.emptyLibraryView];
338
        self.navigationItem.rightBarButtonItem = nil;
339
340
    } else
        self.navigationItem.rightBarButtonItem = self.editButtonItem;
341
342
343
344

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        _tableView.separatorStyle = (_foundMedia.count > 0)? UITableViewCellSeparatorStyleSingleLine:
                                                             UITableViewCellSeparatorStyleNone;
345
346
    } else
        [self.collectionView.collectionViewLayout invalidateLayout];
347
348
}

349
350
- (void)libraryUpgradeComplete
{
Gleb Pinigin's avatar
Gleb Pinigin committed
351
352
    self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", @"");
    self.navigationItem.leftBarButtonItem = _menuButton;
353
354
355
356
357
    self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = NO;
    self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", @"");
    [self.emptyLibraryView.activityIndicator stopAnimating];
    [self.emptyLibraryView removeFromSuperview];

358
359
360
361
362
363
    [self updateViewContents];
}

- (void)libraryWasUpdated
{
    [self updateViewContents];
364
365
}

366
- (void)updateViewContents
Felix Paul Kühne's avatar
Felix Paul Kühne committed
367
{
368
369
    _foundMedia = [[NSMutableArray alloc] init];

370
371
372
373
374
375
376
377
    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", @"");

378
379
    /* add all albums */
    if (_libraryMode != VLCLibraryModeAllSeries) {
380
        NSArray *rawAlbums = [MLAlbum allAlbums];
381
        for (MLAlbum *album in rawAlbums) {
Carola's avatar
Carola committed
382
            if (album.name.length > 0 && album.tracks.count > 1)
383
384
                [_foundMedia addObject:album];
        }
385
386
387
388
389
    }
    if (_libraryMode == VLCLibraryModeAllAlbums) {
        [self reloadViews];
        return;
    }
390

391
392
393
    /* add all shows */
    NSArray *rawShows = [MLShow allShows];
    for (MLShow *show in rawShows) {
Carola's avatar
Carola committed
394
        if (show.name.length > 0 && show.episodes.count > 1)
395
396
397
398
399
400
401
            [_foundMedia addObject:show];
    }
    if (_libraryMode == VLCLibraryModeAllSeries) {
        [self reloadViews];
        return;
    }

Carola's avatar
Carola committed
402
403
404
405
406
    /* add all folders*/
    NSArray *allFolders = [MLLabel allLabels];
    for (MLLabel *folder in allFolders)
        [_foundMedia addObject:folder];

407
408
409
    /* add all remaining files */
    NSArray *allFiles = [MLFile allFiles];
    for (MLFile *file in allFiles) {
Carola's avatar
Carola committed
410
411
        if (file.labels.count > 0) continue;

412
413
        if (!file.isShowEpisode && !file.isAlbumTrack)
            [_foundMedia addObject:file];
414
415
416
        else if (file.isShowEpisode) {
            if (file.showEpisode.show.episodes.count < 2)
                [_foundMedia addObject:file];
417
418
419
420
421
422
423
424
425

            /* 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];
            }
426
427
428
429
        } else if (file.isAlbumTrack) {
            if (file.albumTrack.album.tracks.count < 2)
                [_foundMedia addObject:file];
        }
430
    }
431

432
    [self reloadViews];
433
}
434

435
- (void)reloadViews
436
{
437
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
438
        [self.tableView reloadData];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
439
440
    else
        [self.collectionView reloadData];
441
442

    [self _displayEmptyLibraryViewIfNeeded];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
443
444
}

445
446
#pragma mark - Table View

Felix Paul Kühne's avatar
Felix Paul Kühne committed
447
448
449
450
451
452
453
454
455
456
457
458
459
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _foundMedia.count;
}

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

462
    VLCPlaylistTableViewCell *cell = (VLCPlaylistTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
463
    if (cell == nil)
464
        cell = [VLCPlaylistTableViewCell cellWithReuseIdentifier:CellIdentifier];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
465

466
467
468
469
    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightGestureAction:)];
    [swipeRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
    [cell addGestureRecognizer:swipeRight];

470
471
    NSInteger row = indexPath.row;
    cell.mediaObject = _foundMedia[row];
472

Felix Paul Kühne's avatar
Felix Paul Kühne committed
473
474
475
    return cell;
}

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
- (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;
}

491
492
493
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor colorWithWhite:.122 alpha:1.];
494
    cell.multipleSelectionBackgroundView.backgroundColor = cell.backgroundColor;
495
496
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
497
498
499
500
501
502
503
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

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

508
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Felix Paul Kühne's avatar
Felix Paul Kühne committed
509
{
Carola's avatar
Carola committed
510
511
512
513
514
515
516
    if (tableView.isEditing) {
        if (_libraryMode == VLCLibraryModeCreateFolder) {
            _folderObject = _foundMedia[indexPath.row];
            _libraryMode = _previousLibraryMode;
            [self updateViewContents];
            [self createFolderWithName:nil];
        }
517
        return;
Carola's avatar
Carola committed
518
    }
519

520
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
521
    NSManagedObject *selectedObject = _foundMedia[indexPath.row];
522
523
524
525
526
527
528
    if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
        VLCMediaList *list;
        NSArray *tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
        NSUInteger count = tracks.count;
        list = [[VLCMediaList alloc] init];

        MLFile *file;
529
        VLCMedia *media;
530
        for (NSInteger x = count - 1; x > -1; x--) {
531
            file = [(MLAlbumTrack*)tracks[x] files].anyObject;
532
533
534
            media = [VLCMedia mediaWithURL: [NSURL URLWithString:file.url]];
            [media parse];
            [list addMedia:media];
535
        }
536
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[tracks indexOfObject:selectedObject]];
Carola's avatar
Carola committed
537
538
539
540
541
542
543
544
545
546
547
548
549
    } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
        VLCMediaList *list;
        MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
        NSArray *folderTracks = [folder sortedFolderItems];
        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]];
550
551
    } else
        [self openMediaObject:selectedObject];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
552
553
}

554
#pragma mark - table view gestures
555
556
557
558
559
560
561
562
563
564
565
566
567
568
- (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];
    }
}

569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
#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
{
588
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
589
        if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
590
591
592
593
            return CGSizeMake(341., 190.);
        else
            return CGSizeMake(384., 216.);
    }
594

595
596
597
598
599
    return CGSizeMake(298.0, 220.0);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
600
601
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return UIEdgeInsetsMake(0., 0., 0., 0.);
602
603
604
    return UIEdgeInsetsMake(0.0, 34.0, 0.0, 34.0);
}

605
606
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
607
608
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return 0.;
609
610
611
612
613
    return 10.0;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
614
615
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return 0.;
616
617
618
    return 10.0;
}

619
620
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
621
    if (self.editing) {
Carola's avatar
Carola committed
622
623
624
        if (_libraryMode == VLCLibraryModeCreateFolder) {
            _folderObject = _foundMedia[indexPath.item];
            [self createFolderWithName:nil];
625
             _libraryMode = _previousLibraryMode;
Carola's avatar
Carola committed
626
        }
627
628
629
630
        [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
        return;
    }

631
    NSManagedObject *selectedObject = _foundMedia[indexPath.row];
632
633
634
635
636
637
638
    if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
        VLCMediaList *list;
        NSArray *tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
        NSUInteger count = tracks.count;
        list = [[VLCMediaList alloc] init];

        MLFile *file;
639
        for (NSInteger x = count - 1; x > -1; x--) {
640
641
642
            file = [(MLAlbumTrack*)tracks[x] files].anyObject;
            [list addMedia:[VLCMedia mediaWithURL: [NSURL URLWithString:file.url]]];
        }
643
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[tracks indexOfObject:selectedObject]];
Carola's avatar
Carola committed
644
645
646
    } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
        VLCMediaList *list;
        MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
Carola's avatar
Carola committed
647
        NSArray *folderTracks = [folder sortedFolderItems];
Carola's avatar
Carola committed
648
649
650
651
652
653
654
655
656
        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]];
657
658
    } else
        [self openMediaObject:selectedObject];
659
660
}

661
662
663
664
665
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
}

Carola's avatar
Carola committed
666
667
668
- (void)collectionView:(UICollectionView *)collectionView removeItemFromFolderAtIndexPathIfNeeded:(NSIndexPath *)indexPath
{
    MLFile *mediaObject = (MLFile *)_foundMedia[indexPath.item];
669
    [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
Carola's avatar
Carola committed
670
    mediaObject.labels = nil;
671
    mediaObject.folderTrackNumber = nil;
Carola's avatar
Carola committed
672
673
674
675

    [self backToAllItems:nil];
}

676
677
678
679
680
681
682
683
684
685
- (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
686
687
- (void)collectionView:(UICollectionView *)collectionView requestToMoveItemAtIndexPath:(NSIndexPath *)itemPath intoFolderAtIndexPath:(NSIndexPath *)folderPath
{
688
    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
689

690
    if (!validFileTypeAtFolderPath) {
Carola's avatar
Carola committed
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
        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]];
706
        file.folderTrackNumber = @([folder.files count] - 1);
Carola's avatar
Carola committed
707
708
709
710
711
712
713
714
715
716
717
        [_foundMedia removeObjectAtIndex:itemPath.item];
        [self updateViewContents];
    } else {
        _folderObject = _foundMedia[folderPath.item];
        _indexPaths = [NSMutableArray arrayWithArray:@[itemPath]];
        [self showCreateFolderAlert];
    }
}

#pragma mark - Folder implementation

718
719
720
721
722
723
724
725
726
727
728
729
- (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
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
- (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;
    }
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
    else
        _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];

760
761
    for (NSInteger i = _indexPaths.count - 1; i >=0; i--) {
        NSIndexPath *path = _indexPaths[i];
Carola's avatar
Carola committed
762
763
764
765
766
767
        id mediaObject;
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
            mediaObject = _foundMedia[path.item];
        else
            mediaObject = _foundMedia[path.row];
        if ([mediaObject isKindOfClass:[MLLabel class]])
768
            [_indexPaths removeObject:path];
Carola's avatar
Carola committed
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
    }

    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
{
    BOOL isPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
    if (isPad)
        _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
    else
        _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];

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

797
    for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
798
799
800
801
        NSIndexPath *path = _indexPaths[i];
        MLFile *file = (MLFile *)_foundMedia[isPad ? path.item : path.row];

        MLLabel *folder = [file.labels anyObject];
802
        [self rearrangeFolderTrackNumbersForRemovedItem:file];
Carola's avatar
Carola committed
803
        file.labels = nil;
804
        file.folderTrackNumber = nil;
Carola's avatar
Carola committed
805
806
807
808
        [_foundMedia removeObject:file];

        if ([folder.files count] == 0) {
            [self removeMediaObject:folder updateDatabase:YES];
809
            [self setEditing:NO];
Carola's avatar
Carola committed
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
            [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) {
834
        NSUInteger folderIndex = [_foundMedia indexOfObject:_folderObject];
Carola's avatar
Carola committed
835
836
837
838
839
        //item got dragged onto item
        if ([_foundMedia[folderIndex] isKindOfClass:[MLFile class]]) {
            MLFile *file = _foundMedia[folderIndex];
            MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
            label.name = folderName;
840

Carola's avatar
Carola committed
841
            file.labels = [NSSet setWithObjects:label,nil];
842
            NSNumber *folderTrackNumber = [NSNumber numberWithInt:(int)[label files].count - 1];
843
            file.folderTrackNumber = folderTrackNumber;
Carola's avatar
Carola committed
844
845
846

            [_foundMedia removeObjectAtIndex:folderIndex];
            [_foundMedia insertObject:label atIndex:folderIndex];
847
848
849
            MLFile *itemFile = _foundMedia[((NSIndexPath *)_indexPaths[0]).item];
            itemFile.labels = file.labels;
            [_foundMedia removeObjectAtIndex:((NSIndexPath *)_indexPaths[0]).item];
850
            itemFile.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
851
852
853
854
855
        } else {
            //item got dragged onto folder or items should be added to folder
            MLLabel *label = _foundMedia[folderIndex];
            [_indexPaths sortUsingSelector:@selector(compare:)];

856
            for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
857
                NSIndexPath *path = _indexPaths[i];
858
                if (_libraryMode != VLCLibraryModeCreateFolder && ![_foundMedia[path.row] isKindOfClass:[MLFile class]])
859
                    continue;
860
861
862
                if (_libraryMode == VLCLibraryModeCreateFolder)
                    [self updateViewContents];

863
864
865
                MLFile *file = _foundMedia[path.row];
                file.labels = [NSSet setWithObjects:label, nil];
                [_foundMedia removeObjectAtIndex:path.row];
866
                file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
867
868
869
870
871
872
873
874
875
876
877
878
            }
        }
        _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:)];

879
            for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
880
881
882
883
                NSIndexPath *path = _indexPaths[i];
                if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
                    MLFile *file = _foundMedia[path.item];
                    file.labels = [NSSet setWithObjects:label, nil];
884
                    file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
885
886
887
888
                    [_foundMedia removeObjectAtIndex:path.item];
                } else {
                    MLFile *file = _foundMedia[path.row];
                    file.labels = [NSSet setWithObjects:label, nil];
889
                    file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
                    [_foundMedia removeObjectAtIndex:path.row];
                }
            }
        }
    }
    _indexPaths = nil;
    [self setEditing:NO];
    [self updateViewContents];
}

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

905
#pragma mark - UI implementation
906
907
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
908
    [super setEditing:editing animated:animated];
909

Felix Paul Kühne's avatar
Felix Paul Kühne committed
910
    UIBarButtonItem *editButton = self.editButtonItem;
911
912
    NSString *editImage = editing? @"doneButton": @"button";
    NSString *editImageHighlight = editing? @"doneButtonHighlight": @"buttonHighlight";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
913
    if (SYSTEM_RUNS_IOS7_OR_LATER)
914
915
916
917
918
919
920
921
        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
922

923
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
924
        NSArray *visibleCells = self.collectionView.visibleCells;
925

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

Felix Paul Kühne's avatar
Felix Paul Kühne committed
929
930
            [aCell setEditing:editing animated:animated];
        }];
931
932
933
934
935
        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) {
936
            [self.collectionView addGestureRecognizer:_longPressGestureRecognizer];
937
938
939
940
941
            NSArray *selectedItems = [self.collectionView indexPathsForSelectedItems];
            NSUInteger count = selectedItems.count;

            for (NSUInteger x = 0; x < count; x++)
                [self.collectionView deselectItemAtIndexPath:selectedItems[x] animated:NO];
942
943
944
        } else
            [self.collectionView removeGestureRecognizer:_longPressGestureRecognizer];

945
946
    } else {
        self.tableView.allowsMultipleSelectionDuringEditing = editing;
947
        [self.tableView setEditing:editing animated:YES];
948
949
        [self.editButtonItem setTitle:editing ? NSLocalizedString(@"BUTTON_CANCEL",@"") : NSLocalizedString(@"BUTTON_EDIT", @"")];
    }
Carola's avatar
Carola committed
950
951
952
953
954
955
956

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

957
    self.navigationController.toolbarHidden = !editing;
958
959
}

960
961
962
963
964
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleDelete;
}

965
- (IBAction)leftButtonAction:(id)sender
966
{
967
    [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController] toggleSidebar:![(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController].sidebarShowing duration:kGHRevealSidebarDefaultAnimationDuration];
968
969
970

    if (self.isEditing)
        [self setEditing:NO animated:YES];
971
972
}

973
974
- (IBAction)backToAllItems:(id)sender
{
Carola's avatar
Carola committed
975
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
976
977
978
979
980
981
982
983
984
        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];
985
        }
Carola's avatar
Carola committed
986
987
988
989
990
991
    }
    inFolder = NO;
    UIBarButtonItem *createFolderItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
    NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
    toolbarItems[0] = createFolderItem;
    self.toolbarItems = toolbarItems;
992
    [self setLibraryMode:_libraryMode];
993
    [self updateViewContents];
994
995
}

996
- (void)_endEditingWithHardReset:(BOOL)hardReset
997
{
998
    [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
999
1000
    if (hardReset)
        [self updateViewContents];
For faster browsing, not all history is shown. View entire blame