source: branches/CoreData/SapphireFrappliance/MetaDataImporting/SapphireImporterDataMenu.m @ 658

Revision 658, 14.8 KB checked in by gbooker, 6 years ago (diff)

Properly background import instead of waiting on background process to complete. This was the intended operation from the beginning

Line 
1/*
2 * SapphireImporterDataMenu.m
3 * Sapphire
4 *
5 * Created by pnmerrill on Jun. 24, 2007.
6 * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License as published by the Free Software Foundation; either version 3 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
14 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15 * Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with this program; if not,
18 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21#import "SapphireImporterDataMenu.h"
22#import <BackRow/BackRow.h>
23#import "SapphireDirectoryMetaData.h"
24#import "SapphireFileMetaData.h"
25#import "SapphireCollectionDirectory.h"
26#import <SapphireCompatClasses/SapphireFrontRowCompat.h>
27#import "SapphireImportHelper.h"
28#import "SapphireApplianceController.h"
29#import "NSString-Extensions.h"
30#import "SapphireMetaDataSupport.h"
31
32@interface BRLayerController (compatounth)
33- (NSRect)controllerFrame;  /*technically wrong; it is really a CGRect*/
34@end
35
36@interface SapphireImporterDataMenu (private)
37- (void)layoutFrame;
38- (void)setFileProgress:(NSString *)updateFileProgress;
39- (void)resetUIElements;
40- (void)pause;
41- (void)itemImportBackgrounded;
42@end
43
44@implementation SapphireImporterDataMenu
45- (id) initWithScene: (BRRenderScene *) scene context:(NSManagedObjectContext *)context  importer:(id <SapphireImporter>)import;
46{
47        if ( [super initWithScene: scene] == nil )
48                return ( nil );
49        moc = [context retain];
50        importer = [import retain];
51        [importer setImporterDataMenu:self];
52        importItems = [[NSMutableArray alloc] init];
53        /*Setup the Header Control with default contents*/
54        [self setListTitle:BRLocalizedString(@"Populate Show Data", @"Do a file metadata import")];
55
56        /*Setup the text entry control*/
57        text = [SapphireFrontRowCompat newTextControlWithScene:scene];
58        fileProgress = [SapphireFrontRowCompat newTextControlWithScene:scene];
59        currentFile = [SapphireFrontRowCompat newTextControlWithScene:scene];
60       
61        /*Setup the progress bar*/
62        bar = [SapphireFrontRowCompat newProgressBarWidgetWithScene:scene];
63        [self layoutFrame];
64       
65        [[self list] setDatasource:self];
66       
67        /*add controls*/
68        [self addControl: text];
69        [self addControl: fileProgress] ;
70        [self addControl: currentFile] ;
71        [SapphireFrontRowCompat addSublayer:bar toControl:self];
72
73    return ( self );
74}
75
76- (void) dealloc
77{
78        [text release];
79        [fileProgress release];
80        [currentFile release];
81        [bar release];
82        [moc release];
83        [collectionDirectories release];
84        [skipSet release];
85        [importItems release];
86        [importTimer invalidate];
87        [importer setImporterDataMenu:nil];
88        [importer release];
89        [buttonTitle release];
90        [super dealloc];
91}
92
93- (void)layoutFrame
94{
95        /*title*/
96        NSRect frame = [SapphireFrontRowCompat frameOfController:self];
97        frame.origin.y += frame.size.height * 5.0f / 16.0f;
98        frame.origin.x = frame.size.width / 6.0f;
99        frame.size.height = frame.size.height / 16.0f;
100        frame.size.width = frame.size.width * 2.0f / 3.0f;
101        [bar setFrame: frame] ;
102}
103
104/*!
105 * @brief Sets the informative text
106 *
107 * @param theText The text to set
108 */
109- (void)setText:(NSString *)theText
110{
111        [SapphireFrontRowCompat setText:theText withAtrributes:[[BRThemeInfo sharedTheme] paragraphTextAttributes] forControl:text];
112       
113        NSRect master = [SapphireFrontRowCompat frameOfController:self];
114        NSSize txtSize = [SapphireFrontRowCompat textControl:text renderedSizeWithMaxSize:NSMakeSize(master.size.width * 2.0f/3.0f, master.size.height * 0.4f)];
115       
116        NSRect frame;
117        frame.origin.x = (master.size.width - txtSize.width) * 0.5f;
118        frame.origin.y = (master.size.height * 0.4f - txtSize.height) + master.size.height * 0.3f/0.8f + master.origin.y;
119        frame.size = txtSize;
120        [text setFrame:frame];
121}
122
123/*!
124 * @brief Sets the file progress string
125 *
126 * @param theFileProgress The file progress string to display
127 */
128- (void)setFileProgress:(NSString *)theFileProgress
129{
130        [SapphireFrontRowCompat setText:theFileProgress withAtrributes:[[BRThemeInfo sharedTheme] paragraphTextAttributes] forControl:fileProgress];
131       
132        NSRect master = [SapphireFrontRowCompat frameOfController:self];
133        NSSize progressSize = [SapphireFrontRowCompat textControl:fileProgress renderedSizeWithMaxSize:NSMakeSize(master.size.width * 1.0f/2.0f, master.size.height * 0.3f)];
134       
135        NSRect frame;
136        frame.origin.x =  (master.size.width) * 0.1f;
137        frame.origin.y = (master.size.height * 0.12f) + master.origin.y;
138        frame.size = progressSize;
139        [fileProgress setFrame:frame];
140}
141
142/*!
143 * @brief Sets the display of the current file being processed
144 *
145 * @param theCurrentFile The current file being proccessed
146 */
147- (void)setCurrentFile:(NSString *)theCurrentFile
148{
149        [SapphireFrontRowCompat setText:theCurrentFile withAtrributes:[[BRThemeInfo sharedTheme] paragraphTextAttributes] forControl:currentFile];
150       
151        NSRect master = [SapphireFrontRowCompat frameOfController:self];
152        NSSize currentFileSize = [SapphireFrontRowCompat textControl:currentFile renderedSizeWithMaxSize:NSMakeSize(master.size.width * 9.0f/10.0f, master.size.height * 0.3f)];
153       
154        NSRect frame;
155        frame.origin.x =  (master.size.width) * 0.1f;
156        frame.origin.y = (master.size.height * 0.09f) + master.origin.y;
157        frame.size = currentFileSize;
158        [currentFile setFrame:frame];
159}
160
161/*!
162 * @brief Get the list of all files to process
163 */
164- (void)getItems
165{
166        SapphireCollectionDirectory *collection = [collectionDirectories objectAtIndex:collectionIndex];
167        if([collection isDeleted] || [collection skipValue])
168        {
169                collectionIndex++;
170                [self performSelector:@selector(gotSubFiles:) withObject:[NSArray array] afterDelay:0.0];
171                return;
172        }
173        SapphireDirectoryMetaData *meta = [collection directory];
174        //Prefetch
175/*      NSPredicate *fetchPredicate = [NSPredicate predicateWithFormat:@"path BEGINSWITH %@", [[meta path] stringByAppendingString:@"/"]];
176        NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"path" ascending:YES];
177        doSortedFetchRequest(SapphireDirectoryMetaDataName, moc, fetchPredicate, sort);
178        doSortedFetchRequest(SapphireFileMetaDataName, moc, fetchPredicate, sort);
179        doSortedFetchRequest(SapphireDirectorySymLinkName, moc, fetchPredicate, sort);
180        doSortedFetchRequest(SapphireFileSymLinkName, moc, fetchPredicate, sort);
181        [sort release];*/
182
183        [meta getSubFileMetasWithDelegate:self skipDirectories:skipSet];
184        collectionIndex++;
185}
186
187- (void)setButtonTitle:(NSString *)title
188{
189        if(title != nil)
190        {
191                [buttonTitle release];
192                buttonTitle = [title retain];
193        }
194        BRListControl *list = [self list];
195       
196        [list setHidden:(title == nil)];
197       
198        [list reload];
199}
200
201/*!
202 * @brief Start the import process
203 */
204- (void)import
205{
206        /*Change display*/
207        [self setButtonTitle:BRLocalizedString(@"Cancel Import", @"Cancel the import process")];
208        action = @selector(cancel);
209        [self setFileProgress:BRLocalizedString(@"Initializing...", @"The import is starting")];
210        [SapphireFrontRowCompat renderScene:[self scene]];
211        /*Initialize the import process*/
212        canceled = NO;
213        suspended = NO;
214        collectionIndex = 0;
215        [collectionDirectories release];
216        collectionDirectories = [[SapphireCollectionDirectory availableCollectionDirectoriesInContext:moc] retain];
217        NSArray *skipCol = [SapphireCollectionDirectory skippedCollectionDirectoriesInContext:moc];
218        [skipSet release];
219        skipSet = [NSMutableSet setWithSet:[skipCol valueForKeyPath:@"directory.path"]];
220        [self getItems];
221}
222
223/*!
224 * @brief Metadata delegate method to return final list of files
225 *
226 * @param subs The files which are children of the current directory
227 */
228- (void)gotSubFiles:(NSArray *)subs
229{
230        [importItems addObjectsFromArray:subs];
231        if(collectionIndex != [collectionDirectories count])
232        {
233                [self getItems];
234                return;
235        }
236        updated = 0 ;
237        current = 0;
238        max = [importItems count];
239        if(!canceled)
240                importTimer = [NSTimer scheduledTimerWithTimeInterval:0.0f target:self selector:@selector(importNextItem:) userInfo:nil repeats:NO];
241}
242
243/*!
244 * @brief Metadata delegate method to inform on its scanning progress
245 *
246 * @param dir The current directory it is scanning
247 */
248- (void)scanningDir:(NSString *)dir
249{
250        [self setCurrentFile:[NSString stringWithFormat:BRLocalizedString(@"Scanning Directory: %@", "Current scan import process format, directory"),[NSString stringByCroppingDirectoryPath:dir toLength:3]]];
251        [SapphireFrontRowCompat renderScene:[self scene]];
252}
253
254/*!
255 * @brief Ask if we should cancel the fetching of file listing
256 *
257 * @return YES if the file listing should be canceled, NO otherwise
258 */
259- (BOOL)getSubFilesCanceled
260{
261        return canceled;
262}
263
264/*!
265 * @brief Import a single item
266 *
267 * @return YES if any data was imported, NO otherwise
268 */
269- (BOOL)doImport
270{
271        BOOL ret = NO;
272        SapphireFileMetaData *file = [importItems objectAtIndex:0];
273        if(file.joinedToFile != nil)
274                return NO;
275        @try {
276                ImportState result = [importer importMetaData:file];
277                switch(result)
278                {
279                        case IMPORT_STATE_UPDATED:
280                                ret = YES;
281                                break;
282                        case IMPORT_STATE_NEEDS_SUSPEND:
283                                [self pause];
284                                ret = NO;
285                                break;
286                        case IMPORT_STATE_BACKGROUND:
287                                [self itemImportBackgrounded];
288                                ret = NO;
289                                break;
290                }
291                [SapphireMetaDataSupport save:moc];
292        }
293        @catch (NSException * e) {
294                [SapphireApplianceController logException:e];
295                [e raise];
296        }
297        @finally {
298                return ret;
299        }
300}
301
302/*!
303 * @brief Change the display to show the completion text
304 */
305- (void)setCompletionText
306{
307        [self setText:[importer completionText]];
308}
309
310- (void)updateDisplay
311{
312        /*Check for completion*/
313        if(current == max)
314        {
315                [self setListTitle:BRLocalizedString(@"Import Complete", @"The import is complete")];
316                [self setFileProgress:[NSString stringWithFormat:BRLocalizedString(@"Updated %0.0f Entries.", @"Import complete format with number updated"), updated]];
317                [self setCurrentFile:@""];
318                [self setCompletionText];
319                [bar setPercentage:100.0f];
320                [self setButtonTitle:nil];
321                action = NULL;
322
323                [SapphireMetaDataSupport save:moc];
324               
325                NSEnumerator *colEnum = [collectionDirectories objectEnumerator];
326                SapphireCollectionDirectory *col;
327                while((col = [colEnum nextObject]) != nil)
328                        [col.directory refreshAllObjects];
329        }
330        else
331        {
332                if([importItems count])
333                {
334                        SapphireFileMetaData *fileMeta = [importItems objectAtIndex:0];
335                        NSString * fileName=[[fileMeta path] lastPathComponent] ;
336                        [self setCurrentFile:[NSString stringWithFormat:BRLocalizedString(@"Fetching For: %@", "Current TV Show import process format, filename"),fileName]];           
337                }
338                else
339                {
340                        [self setCurrentFile:BRLocalizedString(@"Waiting for background import to complete", @"The import is complete, just waiting on background processes")];
341                }
342                [self setFileProgress:[NSString stringWithFormat:BRLocalizedString(@"Finished Processing: %0.0f / %0.0f", @"Import progress format, current and the max"), current, max,updated]];
343                [bar setPercentage:current/max * 100.0f];
344        }
345        [SapphireFrontRowCompat renderScene:[self scene]];             
346}
347
348/*!
349 * @brief Timer function to start the import of the next file
350 *
351 * @param timer The timer that triggered this
352 */
353- (void)importNextItem:(NSTimer *)timer
354{
355        @try
356        {
357                [importTimer invalidate];
358                importTimer = nil;
359
360                /*Update the display*/
361                [self updateDisplay];
362
363                if([importItems count])
364                {
365                        current++ ;
366                        /*Update the imported count*/
367                        if([self doImport] && !backgrounded)
368                                updated++;             
369                       
370                        /*Check for a suspend and reimport afterwards*/
371                        if(suspended || backgrounded)
372                        {
373                                backgrounded = NO;
374                                current--;
375                                if(suspended)
376                                        return;
377                        }
378                       
379                        /*Start with the first item*/
380                        [importItems removeObjectAtIndex:0];
381                        importTimer = [NSTimer scheduledTimerWithTimeInterval:0.0f target:self selector:@selector(importNextItem:) userInfo:nil repeats:NO];
382                }
383        }
384        @catch(NSException *e)
385        {
386                [SapphireApplianceController logException:e];
387        }
388}
389
390/*!
391 * @brief Cancel the import process
392 */
393- (void)cancel
394{
395        /*Kill the timer*/
396        canceled = YES;
397        [importTimer invalidate];
398        importTimer = nil;
399        [importItems removeAllObjects];
400        [[SapphireImportHelper sharedHelperForContext:moc] removeObjectsWithInform:self];
401        /*Reset the display and write data*/
402        [self resetUIElements];
403        [SapphireMetaDataSupport save:moc];
404}
405
406- (void)pause
407{
408        /*Kil lthe timer*/
409        suspended = YES;
410}
411
412- (void)resume
413{
414        /*Sanity checks*/
415        [importTimer invalidate];
416        /*Resume*/
417        suspended = NO;
418        importTimer = [NSTimer scheduledTimerWithTimeInterval:0.0f target:self selector:@selector(importNextItem:) userInfo:nil repeats:NO];
419}
420
421- (oneway void)informComplete:(BOOL)fileUpdated
422{
423        if(fileUpdated)
424                updated++;
425        current++;
426        [self updateDisplay];
427}
428
429- (void)itemImportBackgrounded
430{
431        backgrounded = YES;
432}
433
434- (void)skipNextItem
435{
436        /*Remove the next item from the queue*/
437        if([importItems count])
438                [importItems removeObjectAtIndex:0];
439        current++;
440}
441
442/*!
443 * @brief Reset the UI after an import completion or cancel
444 */
445- (void)resetUIElements
446{
447        [self setFileProgress:@" "];
448        [self setCurrentFile:@" "] ;
449        [bar setPercentage:0.0f];
450        action = @selector(import);
451        [self setListTitle:[importer initialText]];
452        [self setText:[importer informativeText]];
453        [self setButtonTitle:[importer buttonTitle]];
454}
455
456- (void)wasPushed
457{
458        [self layoutFrame];
459        [self resetUIElements];
460        [super wasPushed];
461}
462
463- (void)wasPopped
464{
465        /*Someone hit menu, so cancel*/
466        [self cancel];
467        [super wasPopped];
468}
469
470- (void) wasExhumedByPoppingController: (BRLayerController *) controller
471{
472        [importer wasExhumedByPoppingController:controller];
473        [super wasExhumedByPoppingController:controller];
474}
475
476- (BOOL)brEventAction:(BREvent *)event{
477        BREventPageUsageHash hashVal = [event pageUsageHash];
478       
479        if([(BRControllerStack *)[self stack] peekController] != self || action == NULL)
480                hashVal = 0;
481       
482        switch(hashVal)
483        {
484                case kBREventTapPlayPause:
485                case kBREventHoldPlayPause:
486                        [self performSelector:action];
487                        return YES;
488                        break;
489                case kBREventTapMenu:
490                        [self cancel];
491                        break;
492        }
493        return [super brEventAction:event];
494}
495
496- (long) itemCount
497{
498        return 1;
499}
500
501- (id<BRMenuItemLayer>) itemForRow: (long) row
502{
503        BRAdornedMenuItemLayer *result = [SapphireFrontRowCompat textMenuItemForScene:[self scene] folder:NO];
504        [SapphireFrontRowCompat setTitle:buttonTitle forMenu:result];
505       
506        return result;
507}
508
509- (NSString *) titleForRow: (long) row
510{
511       
512        if ( row >= 1 ) return ( nil );
513       
514        NSString *result = buttonTitle ;
515       
516        return [NSString stringWithFormat:@"  ????? %@", result];
517}
518
519- (long) rowForTitle: (NSString *) aTitle
520{
521    long result = -1;
522    long i, count = [self itemCount];
523    for ( i = 0; i < count; i++ )
524    {
525        if ( [aTitle isEqualToString: [self titleForRow: i]] )
526        {
527            result = i;
528            break;
529        }
530    }
531   
532    return ( result );
533}
534
535- (NSRect)listRectWithSize:(NSRect)listFrame inMaster:(NSRect)master
536{
537        listFrame.size.height = master.size.height * 3.0f / 16.0f;
538        listFrame.origin.y = master.size.height / 8.0f;
539        listFrame.size.width = master.size.width / 3.0f;
540        listFrame.origin.x = master.size.width / 3.0f;
541        return listFrame;
542}
543
544@end
Note: See TracBrowser for help on using the repository browser.