source: trunk/SapphireBrowser.m @ 78

Revision 78, 14.7 KB checked in by gbooker, 7 years ago (diff)

Some more optimizations and respect the fast directory setting.

Line 
1//
2//  SapphireBrowser.m
3//  Sapphire
4//
5//  Created by pnmerrill on 6/20/07.
6//  Copyright 2007 __MyCompanyName__. All rights reserved.
7//
8
9#import "SapphireBrowser.h"
10#import <BackRow/BackRow.h>
11#import "SapphireMetaData.h"
12#import "SapphireMarkMenu.h"
13#import "SapphireMedia.h"
14#import "SapphireVideoPlayer.h"
15#import "SapphireMediaPreview.h"
16#import "SapphireTheme.h"
17#import "SapphireSettings.h"
18
19@interface SapphireBrowser (private)
20- (void)reloadDirectoryContents;
21- (void)processFiles:(NSArray *)files;
22- (void)filesProcessed:(NSDictionary *)files;
23- (NSMutableDictionary *)metaDataForPath:(NSString *)path;
24@end
25
26@interface BRTVShowsSortControl (bypassAccess)
27- (BRTVShowsSortSelectorStateLayer *)gimmieDate;
28- (BRTVShowsSortSelectorStateLayer *)gimmieShow;
29- (int)gimmieState;
30@end
31
32@interface BRTVShowsSortSelectorStateLayer (bypassAccess)
33- (BRTextLayer *)gimmieDate;
34- (BRTextLayer *)gimmieShow;
35@end
36
37@interface NSString (PostStrings)
38- (NSString *)URLEncode;
39@end
40
41@implementation NSString (PostStrings)
42- (NSString *)URLEncode
43{
44        NSString *result = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, NULL, CFSTR("?=&+"), kCFStringEncodingUTF8);
45        return [result autorelease];
46}
47@end
48
49@implementation BRTVShowsSortControl (bypassAccess)
50- (BRTVShowsSortSelectorStateLayer *)gimmieDate
51{
52        return _sortedByDateWidget;
53}
54
55- (BRTVShowsSortSelectorStateLayer *)gimmieShow
56{
57        return _sortedByShowWidget;
58}
59
60- (int)gimmieState
61{
62        return _state;
63}
64
65@end
66
67@implementation BRTVShowsSortSelectorStateLayer (bypassAccess)
68- (BRTextLayer *)gimmieDate
69{
70        return _dateLayer;
71}
72
73- (BRTextLayer *)gimmieShow
74{
75        return _showLayer;
76}
77
78@end
79
80@implementation SapphireBrowser
81
82- (void)replaceControlText:(BRTextLayer *)control withString:(NSString *)str
83{
84        NSMutableAttributedString  *dateString = [[control attributedString] mutableCopy];
85        [dateString replaceCharactersInRange:NSMakeRange(0, [dateString length]) withString:str];
86        [control setAttributedString:dateString];
87        [dateString release];
88}
89
90- (id) initWithScene: (BRRenderScene *) scene metaData: (SapphireDirectoryMetaData *)meta
91{
92        return [self initWithScene:scene metaData:meta predicate:NULL];
93}
94
95- (id) initWithScene: (BRRenderScene *) scene metaData: (SapphireDirectoryMetaData *)meta predicate:(SapphirePredicate *)newPredicate;
96{
97        if ( [super initWithScene: scene] == nil ) return ( nil );
98               
99        _names = [NSMutableArray new];
100        items = [NSMutableArray new];
101        metaData = [meta retain];
102        [metaData setDelegate:self];
103        predicate = [newPredicate retain];
104        sort = [[BRTVShowsSortControl alloc] initWithScene:scene state:1];
105
106        NSRect frame = [self masterLayerFrame];
107        NSRect listFrame = [self listFrameForBounds:frame.size];
108        NSRect sortRect;
109        sortRect.size = [sort preferredSizeForScreenHeight:frame.size.height];
110        sortRect.origin.y = listFrame.origin.y * 1.5f;
111        sortRect.origin.x = (listFrame.size.width - sortRect.size.width)/2 + listFrame.origin.x;
112        listFrame.size.height -= listFrame.origin.y;
113        listFrame.origin.y *= 4.0f;
114        [self replaceControlText:[[sort gimmieDate] gimmieDate] withString:@"Select"];
115        [self replaceControlText:[[sort gimmieDate] gimmieShow] withString:@"Mark"];
116        [self replaceControlText:[[sort gimmieShow] gimmieDate] withString:@"Select"];
117        [self replaceControlText:[[sort gimmieShow] gimmieShow] withString:@"Mark"];
118        [sort setFrame:sortRect];
119        [[_listControl layer] setFrame:listFrame];
120        [self addControl:sort];
121       
122        // set the datasource *after* you've setup your array
123        [[self list] setDatasource: self] ;
124               
125        return ( self );
126}
127
128- (void)_doLayout
129{
130        [super _doLayout];
131        NSRect listFrame = [[_listControl layer] frame];
132        listFrame.size.height -= listFrame.origin.y;
133        listFrame.origin.y *= 2;
134        [[_listControl layer] setFrame:listFrame];
135}
136
137- (void)reloadDirectoryContents
138{
139        [metaData reloadDirectoryContents];
140        [_names removeAllObjects];
141        [items removeAllObjects];
142        if(predicate == NULL)
143        {
144                NSArray *dirs = [metaData directories];
145                [_names addObjectsFromArray:dirs];
146                NSArray *files = [metaData files];
147                [_names addObjectsFromArray:files];
148                dirCount = [dirs count];
149                fileCount = [files count];
150        }
151        else
152        {
153                NSArray *dirs = [metaData predicatedDirectories:predicate];
154                [_names addObjectsFromArray:dirs];
155                NSArray *files = [metaData predicatedFiles:predicate];
156                [_names addObjectsFromArray:files];
157                if([[SapphireSettings sharedSettings] fastSwitching])
158                        [_names addObject:@"< Scan for new files >"];
159                dirCount = [dirs count];
160                fileCount = [files count];
161        }
162        int i=0, count=[_names count];
163        for(i=0; i<count; i++)
164        {
165                [items addObject:[NSNull null]];
166        }
167
168        BRListControl *list = [self list];
169        [list reload];
170        if(dirCount && dirCount != [_names count])
171                [list addDividerAtIndex:dirCount];
172        if(predicate != NULL && [[SapphireSettings sharedSettings] fastSwitching])
173                [list addDividerAtIndex:[_names count] -1];
174        [[self scene] renderScene];
175}
176
177- (void) dealloc
178{
179    // always remember to deallocate your resources
180        [_names release];
181        [items release];
182        [metaData release];
183        [predicate release];
184        [sort release];
185    [super dealloc];
186}
187
188- (void) willBePushed
189{
190    // We're about to be placed on screen, but we're not yet there
191   
192    // always call super
193    [super willBePushed];
194        [self reloadDirectoryContents];
195}
196
197- (void) wasPushed
198{
199    // We've just been put on screen, the user can see this controller's content now
200   
201    // always call super
202    [super wasPushed];
203        [metaData resumeDelayedImport];
204}
205
206- (void) willBePopped
207{
208    // The user pressed Menu, but we've not been removed from the screen yet
209   
210    // always call super
211    [super willBePopped];
212        [metaData cancelImport];
213        [metaData setDelegate:nil];
214}
215
216- (void) wasPopped
217{
218    // The user pressed Menu, removing us from the screen
219   
220    // always call super
221    [super wasPopped];
222}
223
224- (void) willBeBuried
225{
226    // The user just chose an option, and we will be taken off the screen
227   
228    // always call super
229        [metaData cancelImport];
230    [super willBeBuried];
231}
232
233- (void) wasBuriedByPushingController: (BRLayerController *) controller
234{
235    // The user chose an option and this controller is no longer on screen
236   
237    // always call super
238    [super wasBuriedByPushingController: controller];
239}
240
241- (void) willBeExhumed
242{
243    // the user pressed Menu, but we've not been revealed yet
244   
245        id controller = [[self stack] peekController];
246        if([controller isKindOfClass:[BRVideoPlayerController class]])
247        {
248                BRVideoPlayer *player = [(BRVideoPlayerController *)controller player];
249                float elapsed = [player elapsedPlaybackTime];
250                float duration = [player trackDuration];
251                if(elapsed / duration > 0.9f)
252                {
253                        [currentPlayFile setWatched:YES];
254                        [[self list] reload];
255                        [[self scene] renderScene];
256                }
257                if(elapsed < duration - 2)
258                        [currentPlayFile setResumeTime:[player elapsedPlaybackTime]];
259                else
260                        [currentPlayFile setResumeTime:0];
261                [currentPlayFile writeMetaData];
262        }
263        [currentPlayFile release];
264        currentPlayFile = nil;
265        [self reloadDirectoryContents];
266    // always call super
267    [super willBeExhumed];
268}
269
270- (void) wasExhumedByPoppingController: (BRLayerController *) controller
271{
272    // handle being revealed when the user presses Menu
273   
274    // always call super
275    [super wasExhumedByPoppingController: controller];
276        if([_names count] == 0)
277                [[self stack] popController];
278        else
279                [metaData resumeDelayedImport];
280}
281
282- (long) itemCount
283{
284    // return the number of items in your menu list here
285        if([_names count])
286                return ( [ _names count]);
287        // Put up an empty item
288        return 1;
289}
290
291- (id<BRMenuItemLayer>) itemForRow: (long) row
292{
293/*
294    // build a BRTextMenuItemLayer or a BRAdornedMenuItemLayer, etc. here
295    // return that object, it will be used to display the list item.
296    return ( nil );
297*/
298        int nameCount = [_names count];
299        if( nameCount == 0)
300        {
301                BRAdornedMenuItemLayer *result = [BRAdornedMenuItemLayer adornedMenuItemWithScene:[self scene]];
302                [[result textItem] setTitle:@"< EMPTY >"];
303                return result;
304        }
305        if( row >= nameCount ) return ( nil ) ;
306       
307        id cached = [items objectAtIndex:row];
308        if(cached != [NSNull null])
309                return cached;
310        NSString *name = [_names objectAtIndex:row];
311        // Pad filename to correcrtly display gem icons
312        BRAdornedMenuItemLayer * result = nil;
313        BOOL watched = NO;
314        BOOL favorite = NO;
315        BOOL gear = NO;
316        BRRenderScene *scene = [self scene];
317        if(row < dirCount)
318        {
319                result = [BRAdornedMenuItemLayer adornedFolderMenuItemWithScene: scene] ;
320                SapphireDirectoryMetaData *meta = [metaData metaDataForDirectory:name];
321                watched = [meta watchedForPredicate:predicate];
322                favorite = [meta favoriteForPredicate:predicate];
323        }
324        else if(row < dirCount + fileCount)
325        {
326                result = [BRAdornedMenuItemLayer adornedMenuItemWithScene: scene] ;
327                SapphireFileMetaData *meta = [metaData metaDataForFile:name];
328                if(meta != nil)
329                {
330                        [[result textItem] setRightJustifiedText:[meta sizeString]];
331                        watched = [meta watched];
332                        favorite = [meta favorite] ;
333                }
334        }
335        else
336        {
337                result = [BRAdornedMenuItemLayer adornedMenuItemWithScene:scene];
338                gear = YES;
339        }
340        SapphireTheme *theme = [SapphireTheme sharedTheme];
341        if(gear) [result setLeftIcon:[theme gearGemForScene:scene]];
342        else if(!watched) [result setLeftIcon:[theme blueGemForScene:scene]];
343        else if(favorite)[result setLeftIcon:[theme yellowGemForScene:scene]];
344        else [result setLeftIcon:[theme redGemForScene:scene]];
345                       
346        // add text
347        name=[@"   " stringByAppendingString: name] ;
348        [[result textItem] setTitle: name] ;
349        [items replaceObjectAtIndex:row withObject:result];
350                               
351        return ( result ) ;
352}
353
354- (NSString *) titleForRow: (long) row
355{
356
357        if ( row >= [ _names count] ) return ( nil );
358       
359        NSString *result = [ _names objectAtIndex: row] ;
360       
361        return ([@"   " stringByAppendingString: result] ) ;
362}
363
364- (long) rowForTitle: (NSString *) title
365{
366    long result = -1;
367    long i, count = [self itemCount];
368    for ( i = 0; i < count; i++ )
369    {
370        if ( [title isEqualToString: [self titleForRow: i]] )
371        {
372            result = i;
373            break;
374        }
375    }
376   
377    return ( result );
378}
379
380- (void) itemSelected: (long) row
381{
382    // This is called when the user presses play/pause on a list item
383       
384        if([_names count] == 0)
385        {
386                [[self stack] popController];
387                return;
388        }
389       
390        NSString *name = [_names objectAtIndex:row];
391//      name=[@"  " stringByAppendingString: name] ;
392        NSString *dir = [metaData path];
393       
394        if([sort gimmieState] == 2)
395        {
396                id meta = nil;
397                if([[metaData directories] containsObject:name])
398                        meta = [metaData metaDataForDirectory:name];
399                else
400                        meta = [metaData metaDataForFile:name];
401                id controller = [[SapphireMarkMenu alloc] initWithScene:[self scene] metaData:meta];
402                [(SapphireMarkMenu *)controller setPredicate:predicate];
403                [[self stack] pushController:controller];
404                [controller release];
405                return;
406        }
407       
408        if(row < dirCount)
409        {
410                id controller = [[SapphireBrowser alloc] initWithScene:[self scene] metaData:[metaData metaDataForDirectory:name] predicate:predicate];
411                [controller setListTitle:name];
412                [controller setListIcon:[self listIcon]];
413                [[self stack] pushController:controller];
414                [controller release];
415        }
416        else if(row < dirCount + fileCount)
417        {
418                BRVideoPlayerController *controller = [[BRVideoPlayerController alloc] initWithScene:[self scene]];
419               
420                currentPlayFile = [[metaData metaDataForFile:name] retain];
421                [controller setAllowsResume:YES];
422               
423                NSString *path = [dir stringByAppendingPathComponent:name];
424               
425                if(![[SapphireSettings sharedSettings] disableAnonymousReporting])
426                {
427                        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://appletv.nanopi.net/show.php"]];
428                        NSString *reqData = [NSString stringWithFormat:@"path=%@", [path URLEncode]];
429                        NSData *postData = [reqData dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
430                       
431                        [request setHTTPMethod:@"POST"];
432                        [request setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"];
433                        [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
434                        [request setHTTPBody:postData];
435                        [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
436                        NSURLDownload *download = [[NSURLDownload alloc] initWithRequest:request delegate:nil];
437                        [download autorelease];
438                }
439               
440                NSURL *url = [NSURL fileURLWithPath:path];
441                SapphireMedia *asset  =[[SapphireMedia alloc] initWithMediaURL:url];
442                [asset setResumeTime:[currentPlayFile resumeTime]];
443
444                SapphireVideoPlayer *player = [[SapphireVideoPlayer alloc] init];
445                [player setMetaData:currentPlayFile];
446                NSError *error = nil;
447                [player setMedia:asset error:&error];
448               
449                [controller setVideoPlayer:player];
450                [[self stack] pushController:controller];
451
452                [asset release];
453                [player release];
454                [controller release];
455        }
456        else
457        {
458                [metaData scanForNewFiles];
459                [self reloadDirectoryContents];
460        }
461}
462
463- (id<BRMediaPreviewController>) previewControllerForItem: (long) row
464{
465    // If subclassing BRMediaMenuController, this function is called when the selection cursor
466    // passes over an item.
467        if(row >= [_names count])
468                return nil;
469        NSString *name = [_names objectAtIndex:row];
470        if(row >= dirCount && row < dirCount + fileCount)
471        {
472                SapphireFileMetaData *fileMeta = [metaData metaDataForFile:name];
473               
474                SapphireMediaPreview *preview = [[SapphireMediaPreview alloc] initWithScene:[self scene]];
475               
476//              NSURL *url = [NSURL fileURLWithPath:@"/System/Library/PrivateFrameworks/BackRow.framework/Resources/Movies.png"];
477                NSURL *url = [NSURL fileURLWithPath:[[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingString:@"/Contents/Resources/ApplianceIcon.png"]];
478                CGImageSourceRef sourceRef = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
479                CGImageRef imageRef = nil;
480                if(sourceRef)
481                {
482                        imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
483                        CFRelease(sourceRef);
484                }
485                if(imageRef)
486                {
487                        [preview setImage:imageRef];
488                        CFRelease(imageRef);
489                }
490               
491                NSMutableDictionary *attrs = [[[BRThemeInfo sharedTheme] metadataSummaryFieldAttributes] mutableCopy];
492                NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
493                float tabLoc = [[self masterLayer] frame].size.width * 0.4f;
494                NSTextTab *myTab = [[NSTextTab alloc] initWithType:NSRightTabStopType location:tabLoc];
495                NSArray *tabs = [NSArray arrayWithObject:myTab];
496                [myTab release];
497                [style setTabStops:tabs];
498                [attrs removeObjectForKey:@"CTTextAlignment"];
499                [attrs setObject:style forKey:NSParagraphStyleAttributeName];
500                [preview setText:[[[NSAttributedString alloc] initWithString:[fileMeta metaDataDescription] attributes:attrs] autorelease]];
501                [attrs release];
502               
503                return [preview autorelease];
504        }
505    return ( nil );
506}
507
508- (BOOL)brEventAction:(id)fp8
509{
510        [metaData resumeDelayedImport];
511        return [super brEventAction:fp8];
512}
513
514- (void)updateCompleteForFile:(NSString *)file
515{
516        int index = [_names indexOfObject:file];
517        if(index != NSNotFound)
518                [items replaceObjectAtIndex:index withObject:[NSNull null]];
519        BRListControl *list = [self list];
520        [list reload];
521        [[self scene] renderScene];
522}
523
524@end
Note: See TracBrowser for help on using the repository browser.