VLCPlaylistViewController.m 63.1 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"
sanmarcos's avatar
sanmarcos committed
29
30
31
#import "VLCOpenInActivity.h"

#import <AssetsLibrary/AssetsLibrary.h>
32

33
34
35
/* prefs keys */
static NSString *kDisplayedFirstSteps = @"Did we display the first steps tutorial?";

36
@implementation EmptyLibraryView
37
38
39
40
41
42
43
44
45
46

- (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];
}

47
48
@end

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

    NSMutableArray *_searchData;
    UISearchBar *_searchBar;
    UISearchDisplayController *_searchDisplayController;
67
68

    BOOL _usingTableViewToShowData;
sanmarcos's avatar
sanmarcos committed
69
70
71

    UIBarButtonItem *_actionBarButtonItem;
    VLCOpenInActivity *_openInActivity;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
72
}
73
74

@property (nonatomic, strong) UITableView *tableView;
75
@property (nonatomic, strong) UICollectionView *collectionView;
76
77
@property (nonatomic, strong) EmptyLibraryView *emptyLibraryView;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
78
79
80
81
@end

@implementation VLCPlaylistViewController

82
83
84
85
86
87
+ (void)initialize
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults registerDefaults:@{kDisplayedFirstSteps : [NSNumber numberWithBool:NO]}];
}

88
89
- (void)loadView
{
90
91
92
    _usingTableViewToShowData = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone;

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

121
    _libraryMode = VLCLibraryModeAllFiles;
122

123
124
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.emptyLibraryView = [[[NSBundle mainBundle] loadNibNamed:@"VLCEmptyLibraryView" owner:self options:nil] lastObject];
125
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
126
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.numberOfLines = 0;
127
128
129
130
}

#pragma mark -

Felix Paul Kühne's avatar
Felix Paul Kühne committed
131
132
133
- (void)viewDidLoad
{
    [super viewDidLoad];
134
    self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", nil);
135
136
    _menuButton = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(leftButtonAction:)];
    self.navigationItem.leftBarButtonItem = _menuButton;
137

Felix Paul Kühne's avatar
Felix Paul Kühne committed
138
    if (SYSTEM_RUNS_IOS7_OR_LATER)
139
140
141
142
143
144
145
        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
146

147
148
    _emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", nil);
    _emptyLibraryView.emptyLibraryLongDescriptionLabel.text = NSLocalizedString(@"EMPTY_LIBRARY_LONG", nil);
149
    [_emptyLibraryView.emptyLibraryLongDescriptionLabel sizeToFit];
150
    [_emptyLibraryView.learnMoreButton setTitle:NSLocalizedString(@"BUTTON_LEARN_MORE", nil) forState:UIControlStateNormal];
Carola's avatar
Carola committed
151
    UIBarButtonItem *createFolderItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemOrganize target:self action:@selector(createFolder)];
sanmarcos's avatar
sanmarcos committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

    // Better visual alignment with the action button
    UIEdgeInsets insets = UIEdgeInsetsMake(4, 0, 0, 0);
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        insets.top += 1;
    }
    createFolderItem.imageInsets = insets;

    _actionBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actOnSelection:)];
    _actionBarButtonItem.enabled = NO;

    // If you find strange issues with multiple Edit/Cancel actions causing UIToolbar spacing corruption, use a flexible space instead of a fixed space.
    UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
    fixedSpace.width = 20;

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        fixedSpace.width *= 2;
    }

171
    [self setToolbarItems:@[_actionBarButtonItem, fixedSpace, createFolderItem, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_RENAME", nil) 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
172
    self.navigationController.toolbar.barStyle = UIBarStyleBlack;
173

174
175
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
        self.navigationController.toolbar.tintColor = [UIColor whiteColor];
176
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
177
178
    } else
        [self.navigationController.toolbar setBackgroundImage:[UIImage imageNamed:@"bottomBlackBar"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
179

180
    if (_usingTableViewToShowData) {
181
182
        _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
        UINavigationBar *navBar = self.navigationController.navigationBar;
183
184
185
        if (SYSTEM_RUNS_IOS7_OR_LATER) {
            _searchBar.barTintColor = navBar.barTintColor;
        }
186
187
188
189
190
191
192
        _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;
193
194
        _searchDisplayController.searchResultsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        _searchDisplayController.searchResultsTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
195
        _searchBar.delegate = self;
196
197
198
199

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

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

205
206
207
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
208
    [self.collectionView.collectionViewLayout invalidateLayout];
209
    [self _displayEmptyLibraryViewIfNeeded];
210
211
}

212
213
- (void)viewDidAppear:(BOOL)animated
{
214
215
    [super viewDidAppear:animated];

216
217
218
219
220
221
222
    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];
    }

223
224
225
    if ([[MLMediaLibrary sharedMediaLibrary] libraryNeedsUpgrade]) {
        self.navigationItem.rightBarButtonItem = nil;
        self.navigationItem.leftBarButtonItem = nil;
Gleb Pinigin's avatar
Gleb Pinigin committed
226
        self.title = @"";
227
        self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"UPGRADING_LIBRARY", nil);
228
229
        self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = YES;
        [self.emptyLibraryView.activityIndicator startAnimating];
Gleb Pinigin's avatar
Gleb Pinigin committed
230
        self.emptyLibraryView.frame = self.view.bounds;
231
232
233
234
235
236
237
        [self.view addSubview:self.emptyLibraryView];

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

238
    if (_foundMedia.count < 1)
239
        [self updateViewContents];
240
241
242
243
244
245
246
247
248
    [[MLMediaLibrary sharedMediaLibrary] performSelector:@selector(libraryDidAppear) withObject:nil afterDelay:1.];
}

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

249
250
251
252
253
254
255
256
257
258
259
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

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

260
261
262
- (void)openMediaObject:(NSManagedObject *)mediaObject
{
    if ([mediaObject isKindOfClass:[MLAlbum class]]) {
263
        _foundMedia = [NSMutableArray arrayWithArray:[(MLAlbum *)mediaObject sortedTracks]];
264
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
265
        if (_libraryMode == VLCLibraryModeAllFiles)
266
            self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", nil);
267
        else
268
            [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_MUSIC", nil)];
269
270
271
        self.title = [(MLAlbum*)mediaObject name];
        [self reloadViews];
    } else if ([mediaObject isKindOfClass:[MLShow class]]) {
272
        _foundMedia = [NSMutableArray arrayWithArray:[(MLShow *)mediaObject sortedEpisodes]];
273
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
274
        if (_libraryMode == VLCLibraryModeAllFiles)
275
            self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", nil);
276
        else
277
            [self.navigationItem.leftBarButtonItem setTitle:NSLocalizedString(@"LIBRARY_SERIES", nil)];
278
279
        self.title = [(MLShow*)mediaObject name];
        [self reloadViews];
Carola's avatar
Carola committed
280
281
282
    } else if ([mediaObject isKindOfClass:[MLLabel class]]) {
        MLLabel *folder = (MLLabel*) mediaObject;
        inFolder = YES;
283
        if (!_usingTableViewToShowData) {
284
285
286
287
288
289
290
            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];
291
            }
Carola's avatar
Carola committed
292
        }
293
        _foundMedia = [NSMutableArray arrayWithArray:[folder sortedFolderItems]];
Carola's avatar
Carola committed
294
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem themedBackButtonWithTarget:self andSelector:@selector(backToAllItems:)];
295
        self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"BUTTON_BACK", nil);
Carola's avatar
Carola committed
296
297
        self.title = [folder name];

298
        UIBarButtonItem *removeFromFolder = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemReply target:self action:@selector(removeFromFolder)];
Carola's avatar
Carola committed
299
        NSMutableArray *toolbarItems = [self.toolbarItems mutableCopy];
sanmarcos's avatar
sanmarcos committed
300
        toolbarItems[2] = removeFromFolder;
Carola's avatar
Carola committed
301
302
303
304
        self.toolbarItems = toolbarItems;

        [self reloadViews];
        return;
305
306
    } else
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaFromManagedObject:mediaObject];
307
308
}

309
- (void)removeMediaObject:(id)managedObject updateDatabase:(BOOL)updateDB
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
{
        // 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
347
348
349
350
351
352
353
354
355
    } 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
356
        [self _deleteMediaObject:managedObject];
357

358
359
360
361
    if (updateDB) {
        [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
        [self updateViewContents];
    }
362
363
364
}

- (void)_deleteMediaObject:(MLFile *)mediaObject
365
{
366
367
368
    if (inFolder)
        [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];

369
370
371
372
373
374
375
    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);
    }];
376
    NSUInteger count = indexSet.count;
377
378
379
380
381
382
383
384
385
    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];
386
387
388
389
}

- (void)_displayEmptyLibraryViewIfNeeded
{
390
391
392
    if (self.emptyLibraryView.superview)
        [self.emptyLibraryView removeFromSuperview];

Gleb Pinigin's avatar
Gleb Pinigin committed
393
    if (_foundMedia.count == 0) {
394
395
        self.emptyLibraryView.emptyLibraryLabel.text = inFolder ? NSLocalizedString(@"FOLDER_EMPTY", nil) : NSLocalizedString(@"EMPTY_LIBRARY", nil);
        self.emptyLibraryView.emptyLibraryLongDescriptionLabel.text = inFolder ? NSLocalizedString(@"FOLDER_EMPTY_LONG", nil) : NSLocalizedString(@"EMPTY_LIBRARY_LONG", nil);
Carola's avatar
Carola committed
396
        self.emptyLibraryView.learnMoreButton.hidden = inFolder;
Gleb Pinigin's avatar
Gleb Pinigin committed
397
        self.emptyLibraryView.frame = self.view.bounds;
398
        [self.view addSubview:self.emptyLibraryView];
399
        self.navigationItem.rightBarButtonItem = nil;
400
401
    } else
        self.navigationItem.rightBarButtonItem = self.editButtonItem;
402

403
    if (_usingTableViewToShowData)
404
405
        _tableView.separatorStyle = (_foundMedia.count > 0)? UITableViewCellSeparatorStyleSingleLine:
                                                             UITableViewCellSeparatorStyleNone;
406
    else
407
        [self.collectionView.collectionViewLayout invalidateLayout];
408
409
}

410
411
- (void)libraryUpgradeComplete
{
412
    self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", nil);
Gleb Pinigin's avatar
Gleb Pinigin committed
413
    self.navigationItem.leftBarButtonItem = _menuButton;
414
    self.emptyLibraryView.emptyLibraryLongDescriptionLabel.hidden = NO;
415
    self.emptyLibraryView.emptyLibraryLabel.text = NSLocalizedString(@"EMPTY_LIBRARY", nil);
416
417
418
    [self.emptyLibraryView.activityIndicator stopAnimating];
    [self.emptyLibraryView removeFromSuperview];

419
420
421
422
423
424
    [self updateViewContents];
}

- (void)libraryWasUpdated
{
    [self updateViewContents];
425
426
}

427
- (void)updateViewContents
Felix Paul Kühne's avatar
Felix Paul Kühne committed
428
{
429
430
    _foundMedia = [[NSMutableArray alloc] init];

431
432
    self.navigationItem.leftBarButtonItem = _menuButton;
    if (_libraryMode == VLCLibraryModeAllAlbums)
433
        self.title = NSLocalizedString(@"LIBRARY_MUSIC", nil);
434
    else if( _libraryMode == VLCLibraryModeAllSeries)
435
        self.title = NSLocalizedString(@"LIBRARY_SERIES", nil);
436
    else
437
        self.title = NSLocalizedString(@"LIBRARY_ALL_FILES", nil);
438

439
440
    /* add all albums */
    if (_libraryMode != VLCLibraryModeAllSeries) {
441
        NSArray *rawAlbums = [MLAlbum allAlbums];
442
        for (MLAlbum *album in rawAlbums) {
Carola's avatar
Carola committed
443
            if (album.name.length > 0 && album.tracks.count > 1)
444
445
                [_foundMedia addObject:album];
        }
446
447
448
449
450
    }
    if (_libraryMode == VLCLibraryModeAllAlbums) {
        [self reloadViews];
        return;
    }
451

452
453
454
    /* add all shows */
    NSArray *rawShows = [MLShow allShows];
    for (MLShow *show in rawShows) {
Carola's avatar
Carola committed
455
        if (show.name.length > 0 && show.episodes.count > 1)
456
457
458
459
460
461
462
            [_foundMedia addObject:show];
    }
    if (_libraryMode == VLCLibraryModeAllSeries) {
        [self reloadViews];
        return;
    }

Carola's avatar
Carola committed
463
464
465
466
467
    /* add all folders*/
    NSArray *allFolders = [MLLabel allLabels];
    for (MLLabel *folder in allFolders)
        [_foundMedia addObject:folder];

468
469
470
    /* add all remaining files */
    NSArray *allFiles = [MLFile allFiles];
    for (MLFile *file in allFiles) {
Carola's avatar
Carola committed
471
472
        if (file.labels.count > 0) continue;

473
474
        if (!file.isShowEpisode && !file.isAlbumTrack)
            [_foundMedia addObject:file];
475
476
477
        else if (file.isShowEpisode) {
            if (file.showEpisode.show.episodes.count < 2)
                [_foundMedia addObject:file];
478
479
480
481
482
483

            /* 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) {
484
                file.showEpisode.show.name = NSLocalizedString(@"UNTITLED_SHOW", nil);
485
486
                [self performSelector:@selector(updateViewContents) withObject:nil afterDelay:0.1];
            }
487
488
489
490
        } else if (file.isAlbumTrack) {
            if (file.albumTrack.album.tracks.count < 2)
                [_foundMedia addObject:file];
        }
491
    }
492

493
    [self reloadViews];
494
}
495

496
- (void)reloadViews
497
{
sanmarcos's avatar
sanmarcos committed
498
499
    // Since this gets can get called at any point and wipe away the selections, we update the actionBarButtonItem here because this can happen if you tap "Save Video" in the UIActivityController and a media access alert view takes away focus (the corresponding 'became active' notification of UIApplication will call this). Or simply try bringing down the notification center to trigger this. Any existing UIActivityViewController session should be safe as it would have copies of the selected file references.
    if (_usingTableViewToShowData) {
500
        [self.tableView reloadData];
sanmarcos's avatar
sanmarcos committed
501
502
        [self updateActionBarButtonItemStateWithSelectedIndexPaths:[self.tableView indexPathsForSelectedRows]];
    } else {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
503
        [self.collectionView reloadData];
sanmarcos's avatar
sanmarcos committed
504
505
        [self updateActionBarButtonItemStateWithSelectedIndexPaths:[self.collectionView indexPathsForSelectedItems]];
    }
506
507

    [self _displayEmptyLibraryViewIfNeeded];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
508
509
}

510
511
#pragma mark - Table View

Felix Paul Kühne's avatar
Felix Paul Kühne committed
512
513
514
515
516
517
518
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
519
520
521
    if (tableView == self.searchDisplayController.searchResultsTableView)
        return _searchData.count;

Felix Paul Kühne's avatar
Felix Paul Kühne committed
522
523
524
525
526
527
    return _foundMedia.count;
}

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

530
    VLCPlaylistTableViewCell *cell = (VLCPlaylistTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
531
    if (cell == nil)
532
        cell = [VLCPlaylistTableViewCell cellWithReuseIdentifier:CellIdentifier];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
533

534
535
536
537
    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightGestureAction:)];
    [swipeRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
    [cell addGestureRecognizer:swipeRight];

538
    NSInteger row = indexPath.row;
539
540
541
542
543

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

Felix Paul Kühne's avatar
Felix Paul Kühne committed
545
546
547
    return cell;
}

548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
- (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;
}

563
564
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
565
    cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor VLCDarkBackgroundColor];
566
    cell.multipleSelectionBackgroundView.backgroundColor = cell.backgroundColor;
567
568
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
569
570
571
572
573
574
575
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

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

sanmarcos's avatar
sanmarcos committed
580
581
582
583
584
585
586
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView.isEditing) {
        [self updateActionBarButtonItemStateWithSelectedIndexPaths:[tableView indexPathsForSelectedRows]];
    }
}

587
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Felix Paul Kühne's avatar
Felix Paul Kühne committed
588
{
Carola's avatar
Carola committed
589
590
591
592
593
594
    if (tableView.isEditing) {
        if (_libraryMode == VLCLibraryModeCreateFolder) {
            _folderObject = _foundMedia[indexPath.row];
            _libraryMode = _previousLibraryMode;
            [self updateViewContents];
            [self createFolderWithName:nil];
sanmarcos's avatar
sanmarcos committed
595
596
        } else {
            [self updateActionBarButtonItemStateWithSelectedIndexPaths:[tableView indexPathsForSelectedRows]];
Carola's avatar
Carola committed
597
        }
598
        return;
Carola's avatar
Carola committed
599
    }
600

601
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
602
603
604
605
606
607
    NSManagedObject *selectedObject;
    if (tableView == self.searchDisplayController.searchResultsTableView)
        selectedObject = _searchData[indexPath.row];
    else
        selectedObject = _foundMedia[indexPath.row];

608
    if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
609
610
611
612
        _tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
        NSUInteger count = _tracks.count;
        _list = [[VLCMediaList alloc] init];
        _list.delegate = self;
613
        MLFile *file;
614
        VLCMedia *media;
615
        for (NSInteger x = count - 1; x > -1; x--) {
616
            file = [(MLAlbumTrack*)_tracks[x] files].anyObject;
617
618
            media = [VLCMedia mediaWithURL: [NSURL URLWithString:file.url]];
            [media parse];
619
            [_list addMedia:media];
620
        }
621
        _mediaToPlayIndex = indexPath.row;
Carola's avatar
Carola committed
622
623
    } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
        MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
624
625
626
627
        _tracks = [folder sortedFolderItems];
        NSUInteger count = _tracks.count;
        _list = [[VLCMediaList alloc] init];
        _list.delegate = self;
Carola's avatar
Carola committed
628
629
        MLFile *file;
        for (NSInteger x = count - 1; x > -1; x--) {
630
631
            file = (MLFile *)_tracks[x];
            [_list addMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:file.url]]];
Carola's avatar
Carola committed
632
        }
633
        _mediaToPlayIndex = indexPath.row;
634
635
636
637
638
    }

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

641
642
643
644
645
646
647
648
649
#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]];
    }
}

650
#pragma mark - Gesture Action
651
652
- (void)swipeRightGestureAction:(UIGestureRecognizer *)recognizer
{
653
    if ([[self.editButtonItem title] isEqualToString:NSLocalizedString(@"BUTTON_CANCEL", nil)])
654
655
656
657
658
659
660
661
662
663
664
        [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];
    }
}

665
666
667
668
669
670
671
672
673
- (void)tapTwiceGestureAction:(UIGestureRecognizer *)recognizer
{
    _searchBar.hidden = !_searchBar.hidden;
    if (_searchBar.hidden)
        self.tableView.tableHeaderView = nil;
    else
        self.tableView.tableHeaderView = _searchBar;
}

674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
#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
{
693
    if (SYSTEM_RUNS_IOS7_OR_LATER) {
694
        if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
695
696
697
698
            return CGSizeMake(341., 190.);
        else
            return CGSizeMake(384., 216.);
    }
699

700
701
702
703
704
    return CGSizeMake(298.0, 220.0);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
705
    if (SYSTEM_RUNS_IOS7_OR_LATER)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
706
        return UIEdgeInsetsZero;
707
708
709
    return UIEdgeInsetsMake(0.0, 34.0, 0.0, 34.0);
}

710
711
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
712
713
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return 0.;
714
715
716
717
718
    return 10.0;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
719
720
    if (SYSTEM_RUNS_IOS7_OR_LATER)
        return 0.;
721
722
723
    return 10.0;
}

724
725
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
726
    if (self.editing) {
Carola's avatar
Carola committed
727
728
729
        if (_libraryMode == VLCLibraryModeCreateFolder) {
            _folderObject = _foundMedia[indexPath.item];
            [self createFolderWithName:nil];
730
             _libraryMode = _previousLibraryMode;
sanmarcos's avatar
sanmarcos committed
731
732
        } else {
            [self updateActionBarButtonItemStateWithSelectedIndexPaths:[collectionView indexPathsForSelectedItems]];
Carola's avatar
Carola committed
733
        }
734
735
736
737
        [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
        return;
    }

738
    NSManagedObject *selectedObject = _foundMedia[indexPath.row];
739
740
741
742
743
744
745
    if ([selectedObject isKindOfClass:[MLAlbumTrack class]]) {
        VLCMediaList *list;
        NSArray *tracks = [[(MLAlbumTrack*)selectedObject album] sortedTracks];
        NSUInteger count = tracks.count;
        list = [[VLCMediaList alloc] init];

        MLFile *file;
746
        for (NSInteger x = count - 1; x > -1; x--) {
747
748
749
            file = [(MLAlbumTrack*)tracks[x] files].anyObject;
            [list addMedia:[VLCMedia mediaWithURL: [NSURL URLWithString:file.url]]];
        }
750
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate openMediaList:list atIndex:(int)[tracks indexOfObject:selectedObject]];
Carola's avatar
Carola committed
751
752
753
    } else if ([selectedObject isKindOfClass:[MLFile class]] && [((MLFile *)selectedObject).labels count] > 0) {
        VLCMediaList *list;
        MLLabel *folder = [((MLFile *)selectedObject).labels anyObject];
Carola's avatar
Carola committed
754
        NSArray *folderTracks = [folder sortedFolderItems];
Carola's avatar
Carola committed
755
756
757
758
759
760
761
762
763
        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]];
764
765
    } else
        [self openMediaObject:selectedObject];
766
767
}

768
769
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
sanmarcos's avatar
sanmarcos committed
770
771
772
773
    if (self.editing) {
        [self updateActionBarButtonItemStateWithSelectedIndexPaths:[collectionView indexPathsForSelectedItems]];
    }

774
775
776
    [(VLCPlaylistCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath] selectionUpdate];
}

Carola's avatar
Carola committed
777
778
779
- (void)collectionView:(UICollectionView *)collectionView removeItemFromFolderAtIndexPathIfNeeded:(NSIndexPath *)indexPath
{
    MLFile *mediaObject = (MLFile *)_foundMedia[indexPath.item];
780
    [self rearrangeFolderTrackNumbersForRemovedItem:mediaObject];
Carola's avatar
Carola committed
781
    mediaObject.labels = nil;
782
    mediaObject.folderTrackNumber = nil;
Carola's avatar
Carola committed
783
784
785
786

    [self backToAllItems:nil];
}

787
788
789
790
791
792
793
794
795
796
- (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
797
798
- (void)collectionView:(UICollectionView *)collectionView requestToMoveItemAtIndexPath:(NSIndexPath *)itemPath intoFolderAtIndexPath:(NSIndexPath *)folderPath
{
799
    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
800

801
    if (!validFileTypeAtFolderPath) {
802
        VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_INVALID_TYPE_TITLE", nil) message:NSLocalizedString(@"FOLDER_INVALID_TYPE_MESSAGE", nil) cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", nil)]];
Carola's avatar
Carola committed
803
804
805
806
807
808
809
810
811
812
813
814
815
816

        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]];
817
        file.folderTrackNumber = @([folder.files count] - 1);
Carola's avatar
Carola committed
818
819
820
821
822
823
824
825
826
827
828
        [_foundMedia removeObjectAtIndex:itemPath.item];
        [self updateViewContents];
    } else {
        _folderObject = _foundMedia[folderPath.item];
        _indexPaths = [NSMutableArray arrayWithArray:@[itemPath]];
        [self showCreateFolderAlert];
    }
}

#pragma mark - Folder implementation

829
830
831
832
833
834
835
836
837
838
839
840
- (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
841
842
- (void)showCreateFolderAlert
{
843
    VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_CHOOSE_NAME_TITLE", nil) message:NSLocalizedString(@"FOLDER_CHOOSE_NAME_MESSAGE", nil) cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) otherButtonTitles:@[NSLocalizedString(@"BUTTON_SAVE", nil)]];
Carola's avatar
Carola committed
844
    [alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
845
    [[alert textFieldAtIndex:0] setText:NSLocalizedString(@"FOLDER_NAME_PLACEHOLDER", nil)];
Carola's avatar
Carola committed
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
    [[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;
    }
866
    if (!_usingTableViewToShowData)
Carola's avatar
Carola committed
867
868
869
870
        _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
    else
        _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];

871
872
    for (NSInteger i = _indexPaths.count - 1; i >=0; i--) {
        NSIndexPath *path = _indexPaths[i];
Carola's avatar
Carola committed
873
        id mediaObject;
874
        if (!_usingTableViewToShowData)
Carola's avatar
Carola committed
875
876
877
878
            mediaObject = _foundMedia[path.item];
        else
            mediaObject = _foundMedia[path.row];
        if ([mediaObject isKindOfClass:[MLLabel class]])
879
            [_indexPaths removeObject:path];
Carola's avatar
Carola committed
880
881
882
883
884
885
886
    }

    if ([_indexPaths count] != 0) {
        NSArray *folder = [MLLabel allLabels];
        //if we already have folders display them
        if ([folder count] > 0) {
            _foundMedia = [NSMutableArray arrayWithArray:folder];
887
            self.title = NSLocalizedString(@"SELECT_FOLDER", nil);
Carola's avatar
Carola committed
888
889
890
891
892
893
894
895
896
897
898
899
            _previousLibraryMode = _libraryMode;
            _libraryMode = VLCLibraryModeCreateFolder;
            [self reloadViews];
            return;
        }
    }
    //no selected items or no existing folder ask for foldername
    [self showCreateFolderAlert];
}

- (void)removeFromFolder
{
900
    if (!_usingTableViewToShowData)
Carola's avatar
Carola committed
901
902
903
904
905
906
        _indexPaths = [NSMutableArray arrayWithArray:[self.collectionView indexPathsForSelectedItems]];
    else
        _indexPaths = [NSMutableArray arrayWithArray:[self.tableView indexPathsForSelectedRows]];

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

907
    for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
908
        NSIndexPath *path = _indexPaths[i];
909
        MLFile *file = (MLFile *)_foundMedia[_usingTableViewToShowData ? path.row : path.item];
Carola's avatar
Carola committed
910
911

        MLLabel *folder = [file.labels anyObject];
912
        [self rearrangeFolderTrackNumbersForRemovedItem:file];
Carola's avatar
Carola committed
913
        file.labels = nil;
914
        file.folderTrackNumber = nil;
Carola's avatar
Carola committed
915
916
917
918
        [_foundMedia removeObject:file];

        if ([folder.files count] == 0) {
            [self removeMediaObject:folder updateDatabase:YES];
919
            [self setEditing:NO];
Carola's avatar
Carola committed
920
921
922
923
924
925
926
927
928
929
930
931
932
            [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;
933
            VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_TITLE", nil) message:NSLocalizedString(@"FOLDER_NAME_DUPLICATE_MESSAGE", nil) cancelButtonTitle:nil otherButtonTitles:@[NSLocalizedString(@"BUTTON_OK", nil)]];
Carola's avatar
Carola committed
934
935
936
937
938
939
940
941
942
943

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

    if (_folderObject != nil) {
944
        NSUInteger folderIndex = [_foundMedia indexOfObject:_folderObject];
Carola's avatar
Carola committed
945
946
947
948
949
        //item got dragged onto item
        if ([_foundMedia[folderIndex] isKindOfClass:[MLFile class]]) {
            MLFile *file = _foundMedia[folderIndex];
            MLLabel *label = [[MLMediaLibrary sharedMediaLibrary] createObjectForEntity:@"Label"];
            label.name = folderName;
950

Carola's avatar
Carola committed
951
            file.labels = [NSSet setWithObjects:label,nil];
952
            NSNumber *folderTrackNumber = [NSNumber numberWithInt:(int)[label files].count - 1];
953
            file.folderTrackNumber = folderTrackNumber;
Carola's avatar
Carola committed
954
955
956

            [_foundMedia removeObjectAtIndex:folderIndex];
            [_foundMedia insertObject:label atIndex:folderIndex];
957
958
959
            MLFile *itemFile = _foundMedia[((NSIndexPath *)_indexPaths[0]).item];
            itemFile.labels = file.labels;
            [_foundMedia removeObjectAtIndex:((NSIndexPath *)_indexPaths[0]).item];
960
            itemFile.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
961
962
963
964
965
        } else {
            //item got dragged onto folder or items should be added to folder
            MLLabel *label = _foundMedia[folderIndex];
            [_indexPaths sortUsingSelector:@selector(compare:)];

966
            for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
967
                NSIndexPath *path = _indexPaths[i];
968
                if (_libraryMode != VLCLibraryModeCreateFolder && ![_foundMedia[path.row] isKindOfClass:[MLFile class]])
969
                    continue;
970
971
972
                if (_libraryMode == VLCLibraryModeCreateFolder)
                    [self updateViewContents];

973
974
975
                MLFile *file = _foundMedia[path.row];
                file.labels = [NSSet setWithObjects:label, nil];
                [_foundMedia removeObjectAtIndex:path.row];
976
                file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
977
978
979
980
981
982
983
984
985
986
987
988
            }
        }
        _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:)];

989
            for (NSInteger i = [_indexPaths count] - 1; i >= 0; i--) {
Carola's avatar
Carola committed
990
                NSIndexPath *path = _indexPaths[i];
991
                if (!_usingTableViewToShowData) {
Carola's avatar
Carola committed
992
993
                    MLFile *file = _foundMedia[path.item];
                    file.labels = [NSSet setWithObjects:label, nil];
994
                    file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
995
996
997
998
                    [_foundMedia removeObjectAtIndex:path.item];
                } else {
                    MLFile *file = _foundMedia[path.row];
                    file.labels = [NSSet setWithObjects:label, nil];
999
                    file.folderTrackNumber = @([label files].count - 1);
Carola's avatar
Carola committed
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
                    [_foundMedia removeObjectAtIndex:path.row];
                }
            }
        }
    }
    _indexPaths = nil;
    [self setEditing:NO];
    [self updateViewContents];
}

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

1015
#pragma mark - UI implementation