source: trunk/SapphireVirtualDirectory.m @ 423

Revision 423, 6.7 KB checked in by gbooker, 7 years ago (diff)

Now that the metadata deallocs, I discovered a bug that can happen if a timer retains a virtual sub dir and fires after the parent is dead. Now, when the parent dies, it tell the children so they can die too.

Line 
1/*
2 * SapphireVirtualDirectory.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Nov. 18, 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 "SapphireVirtualDirectory.h"
22
23//Defined in MetaData.m but this is essentially private
24NSString *searchCoverArtExtForPath(NSString *path);
25
26@interface SapphireDirectoryMetaData (privateFunctions)
27- (id)initWithDictionary:(NSDictionary *)dict parent:(SapphireMetaData *)myParent path:(NSString *)myPath;
28@end
29
30@implementation SapphireVirtualDirectory
31- (id)initWithParent:(SapphireVirtualDirectory *)myParent path:(NSString *)myPath
32{
33        self = [super initWithDictionary:nil parent:myParent path:myPath];
34        if(self == nil)
35                return nil;
36       
37        directory = [[NSMutableDictionary alloc] init];
38        reloadTimer = nil;
39        scannedDirectory = YES;
40       
41        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
42        [nc addObserver:self selector:@selector(startedLoading) name:META_DATA_FILE_INFO_STARTED_LOADING object:nil];
43        [nc addObserver:self selector:@selector(finishedLoading) name:META_DATA_FILE_INFO_FINISHED_LOADING object:nil];
44       
45        return self;
46}
47
48- (void) dealloc
49{
50        [[NSNotificationCenter defaultCenter] removeObserver:self];
51        [[directory allValues] makeObjectsPerformSelector:@selector(parentDealloced)];
52        [directory release];
53        [reloadTimer invalidate];
54        [super dealloc];
55}
56
57- (void)parentDealloced
58{
59        parent = nil;
60}
61
62- (void)reloadDirectoryContents
63{
64        [files removeAllObjects];
65        [directories removeAllObjects];
66        [metaFiles removeAllObjects];
67        [metaDirs removeAllObjects];
68        [cachedMetaFiles removeAllObjects];
69        [cachedMetaDirs removeAllObjects];
70        [reloadTimer invalidate];
71        reloadTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(completeReloadOfDirectoryContents) userInfo:nil repeats:NO];
72}
73
74- (void)completeReloadOfDirectoryContents
75{
76        reloadTimer = nil;
77        [delegate directoryContentsChanged];
78}
79
80- (void)setReloadTimer
81{
82        [reloadTimer invalidate];
83        if(!loading)
84                reloadTimer = [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(reloadDirectoryContents) userInfo:nil repeats:NO];
85        else
86                reloadTimer = nil;
87}
88
89- (void)processFile:(SapphireFileMetaData *)file
90{
91}
92
93- (void)removeFile:(SapphireFileMetaData *)file
94{
95}
96
97- (NSString *)classDefaultCoverPath
98{
99        return nil;
100}
101
102- (NSString *)coverArtPathUpToParents:(int)parents
103{
104        NSString *coverPath = searchCoverArtExtForPath([[[SapphireMetaData collectionArtPath] stringByAppendingPathComponent:[self path]] stringByAppendingPathComponent:@"cover"]);
105        if([[NSFileManager defaultManager] fileExistsAtPath:coverPath])
106                return coverPath;
107        if(parents != 0 && [parent isKindOfClass:[SapphireDirectoryMetaData class]])
108                return [(SapphireVirtualDirectory *)parent coverArtPathUpToParents:parents-1];
109        return nil;
110}
111
112- (NSString *)coverArtPath
113{
114        NSString *ret = [self coverArtPathUpToParents:2];
115        if(ret != nil)
116                return ret;
117        return [self classDefaultCoverPath];
118}
119
120- (void)childDisplayChanged
121{
122        /*The way the timings work out, if the timer exists already, it is more efficient to leave it set rather than set a new one*/
123        if(reloadTimer == nil)
124                [self setReloadTimer];
125}
126
127- (NSMutableDictionary *)directoryEntries
128{
129        NSMutableDictionary *ret = [NSMutableDictionary dictionary];
130        NSEnumerator *pathEnum = [directory keyEnumerator];
131        NSString *subPath = nil;
132        while((subPath = [pathEnum nextObject]) != nil)
133        {
134                SapphireMetaData *data = [directory objectForKey:subPath];
135                if([data isKindOfClass:[SapphireVirtualDirectory class]])
136                        [ret setObject:[(SapphireVirtualDirectory *)data directoryEntries] forKey:subPath];
137                else
138                        [ret setObject:[data path] forKey:subPath];
139        }
140        return ret;
141}
142
143- (void)writeToFile:(NSString *)filePath
144{
145        NSMutableDictionary *fileData = [self directoryEntries];
146        [fileData writeToFile:filePath  atomically:YES];
147}
148
149- (BOOL)isDisplayEmpty
150{
151        return [files count] == 0 && [directories count] == 0;
152}
153
154- (BOOL)isEmpty
155{
156        return [directory count] == 0;
157}
158
159- (BOOL)isLoaded
160{
161        return !loading && reloadTimer == nil;
162}
163
164- (void)startedLoading
165{
166        loading = YES;
167}
168
169- (void)finishedLoading
170{
171        if(loading == NO)
172                //Already handled
173                return;
174        loading = NO;
175        [self setReloadTimer];
176}
177
178@end
179
180@implementation SapphireVirtualDirectoryOfDirectories
181- (void)reloadDirectoryContents
182{
183        [super reloadDirectoryContents];
184        NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] init];
185        NSEnumerator *keyEnum = [directory keyEnumerator];
186        NSString *key = nil;
187        while((key = [keyEnum nextObject]) != nil)
188        {
189                SapphireVirtualDirectory *dir = [directory objectForKey:key];
190                if(![dir isDisplayEmpty])
191                        [mutDict setObject:dir forKey:key];
192        }
193        [directories addObjectsFromArray:[mutDict allKeys]];
194        [directories sortUsingSelector:@selector(directoryNameCompare:)];
195        [cachedMetaDirs addEntriesFromDictionary:mutDict];
196        [metaDirs addEntriesFromDictionary:mutDict];
197        [mutDict release];
198        [(SapphireVirtualDirectory *)parent childDisplayChanged];
199}
200
201- (void)finishedLoading
202{
203        NSEnumerator *keyEnum = [directory keyEnumerator];
204        NSString *key = nil;
205        while((key = [keyEnum nextObject]) != nil)
206        {
207                SapphireVirtualDirectory *dir = [directory objectForKey:key];
208                [dir finishedLoading];
209        }
210        [super finishedLoading];
211}
212
213- (BOOL)addFile:(SapphireFileMetaData *)file toKey:(NSString *)key withChildClass:(Class)childClass
214{
215        BOOL added = NO;
216        SapphireVirtualDirectory *child = [directory objectForKey:key];
217        if(child == nil)
218        {
219                child = [[childClass alloc] initWithParent:self path:[[self path] stringByAppendingPathComponent:key]];
220                if(loading)
221                        [child startedLoading];
222                [directory setObject:child forKey:key];
223                [child release];
224                added = YES;
225        }
226        [child processFile:file];
227        if(added == YES)
228        {
229                if([child isEmpty])
230                        [directory removeObjectForKey:key];
231                else
232                        [self setReloadTimer];
233        }
234        return added;
235}
236
237- (BOOL)removeFile:(SapphireFileMetaData *)file fromKey:(NSString *)key
238{
239        SapphireVirtualDirectory *child = [directory objectForKey:key];
240        BOOL ret = NO;
241        if(child != nil)
242        {
243                [child removeFile:file];
244                if([child isEmpty])
245                {
246                        [directory removeObjectForKey:key];
247                        [self setReloadTimer];
248                        ret = YES;
249                }
250        }
251        return ret;
252}
253@end
Note: See TracBrowser for help on using the repository browser.