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

Revision 730, 15.3 KB checked in by gbooker, 5 years ago (diff)

Use the path information of a symlink instead of the file it pointed to in importers
Fixes #211

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