Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 268 lines (207 sloc) 8.321 kB
720b0e7 @uliwitness Added licensing information to the source files.
authored
1 //
2 // UKFNSubscribeFileWatcher.m
3 // Filie
4 //
5 // Created by Uli Kusterer on 03.02.05.
6 // Copyright 2005 Uli Kusterer.
7 //
8 // This software is provided 'as-is', without any express or implied
9 // warranty. In no event will the authors be held liable for any damages
10 // arising from the use of this software.
11 //
12 // Permission is granted to anyone to use this software for any purpose,
13 // including commercial applications, and to alter it and redistribute it
14 // freely, subject to the following restrictions:
15 //
16 // 1. The origin of this software must not be misrepresented; you must not
17 // claim that you wrote the original software. If you use this software
18 // in a product, an acknowledgment in the product documentation would be
19 // appreciated but is not required.
20 //
21 // 2. Altered source versions must be plainly marked as such, and must not be
22 // misrepresented as being the original software.
23 //
24 // 3. This notice may not be removed or altered from any source
25 // distribution.
26 //
dc5be1c Initial check-in.
uli authored
27
28 // -----------------------------------------------------------------------------
29 // Headers:
30 // -----------------------------------------------------------------------------
31
32 #import "UKFNSubscribeFileWatcher.h"
33 #import <Carbon/Carbon.h>
34
35
36 // -----------------------------------------------------------------------------
37 // Private stuff:
38 // -----------------------------------------------------------------------------
39
40 @interface UKFNSubscribeFileWatcher (UKPrivateMethods)
41
42 -(void) sendDelegateMessage: (FNMessage)message forSubscription: (FNSubscriptionRef)subscription;
43
44 @end
45
46
47 // -----------------------------------------------------------------------------
48 // Prototypes:
49 // -----------------------------------------------------------------------------
50
51 void UKFileSubscriptionProc(FNMessage message, OptionBits flags, void *refcon, FNSubscriptionRef subscription);
52
53
54 @implementation UKFNSubscribeFileWatcher
55
56 // -----------------------------------------------------------------------------
57 // sharedFileWatcher:
58 // Singleton accessor.
59 // -----------------------------------------------------------------------------
60
61 +(id) sharedFileWatcher
62 {
63 static UKFNSubscribeFileWatcher* sSharedFileWatcher = nil;
64
65 @synchronized( self )
66 {
67 if( !sSharedFileWatcher )
68 sSharedFileWatcher = [[UKFNSubscribeFileWatcher alloc] init]; // This is a singleton, and thus an intentional "leak".
69 }
70
71 return sSharedFileWatcher;
72 }
73
74
75 // -----------------------------------------------------------------------------
76 // * CONSTRUCTOR:
77 // -----------------------------------------------------------------------------
78
79 -(id) init
80 {
81 self = [super init];
82 if( !self )
83 return nil;
84
85 subscribedPaths = [[NSMutableArray alloc] init];
86 subscribedObjects = [[NSMutableArray alloc] init];
87
88 return self;
89 }
90
91
92 // -----------------------------------------------------------------------------
93 // * DESTRUCTOR:
94 // -----------------------------------------------------------------------------
95
96 -(void) dealloc
97 {
98 [self removeAllPaths];
99
100 [subscribedPaths release];
101 subscribedPaths = nil;
102 [subscribedObjects release];
103 subscribedObjects = nil;
104
105 [super dealloc];
106 }
107
108
109 -(void) finalize
110 {
111 [self removeAllPaths];
112
113 [super finalize];
114 }
115
116 // -----------------------------------------------------------------------------
117 // addPath:
118 // Start watching the object at the specified path. This only sends write
119 // notifications for all changes, as FNSubscribe doesn't tell what actually
120 // changed about our folder.
121 // -----------------------------------------------------------------------------
122
123 -(void) addPath: (NSString*)path
124 {
125 @synchronized( self )
126 {
127 OSStatus err = noErr;
128 static FNSubscriptionUPP subscriptionUPP = NULL;
129 FNSubscriptionRef subscription = NULL;
130
131 if( !subscriptionUPP )
132 subscriptionUPP = NewFNSubscriptionUPP( UKFileSubscriptionProc );
133
134 err = FNSubscribeByPath( (UInt8*) [path fileSystemRepresentation], subscriptionUPP, (void*)self,
135 kNilOptions, &subscription );
136 if( err != noErr )
137 {
138 NSLog( @"UKFNSubscribeFileWatcher addPath: %@ failed due to error ID=%ld.", path, err );
139 return;
140 }
141
142 [subscribedPaths addObject: path];
143 [subscribedObjects addObject: [NSValue valueWithPointer: subscription]];
144 }
145 }
146
147
148 // -----------------------------------------------------------------------------
149 // removePath:
150 // Stop watching the object at the specified path.
151 // -----------------------------------------------------------------------------
152
153 -(void) removePath: (NSString*)path
154 {
155 NSValue* subValue = nil;
156 @synchronized( self )
157 {
158 int idx = [subscribedPaths indexOfObject: path];
159 if( idx != NSNotFound )
160 {
161 subValue = [[[subscribedObjects objectAtIndex: idx] retain] autorelease];
162 [subscribedPaths removeObjectAtIndex: idx];
163 [subscribedObjects removeObjectAtIndex: idx];
164 }
165 }
166
167 if( subValue )
168 {
169 FNSubscriptionRef subscription = [subValue pointerValue];
170
171 FNUnsubscribe( subscription );
172 }
173 }
174
175
176 // -----------------------------------------------------------------------------
177 // delegate:
178 // Accessor for file watcher delegate.
179 // -----------------------------------------------------------------------------
180
181 -(id) delegate
182 {
183 return delegate;
184 }
185
186
187 // -----------------------------------------------------------------------------
188 // setDelegate:
189 // Mutator for file watcher delegate.
190 // -----------------------------------------------------------------------------
191
192 -(void) setDelegate: (id)newDelegate
193 {
194 delegate = newDelegate;
195 }
196
197
198 -(void) removeAllPaths
199 {
200 @synchronized( self )
201 {
202 NSEnumerator* enny = [subscribedObjects objectEnumerator];
203 NSValue* subValue = nil;
204
205 while(( subValue = [enny nextObject] ))
206 {
207 FNSubscriptionRef subscription = [subValue pointerValue];
208 FNUnsubscribe( subscription );
209 }
210
211 [subscribedPaths removeAllObjects];
212 [subscribedObjects removeAllObjects];
213 }
214 }
215
216 @end
217
218 @implementation UKFNSubscribeFileWatcher (UKPrivateMethods)
219
220 // -----------------------------------------------------------------------------
221 // sendDelegateMessage:forSubscription:
222 // Bottleneck for change notifications. This is called by our callback
223 // function to actually inform the delegate and send out notifications.
224 //
225 // This *only* sends out write notifications, as FNSubscribe doesn't tell
226 // what changed about our folder.
227 // -----------------------------------------------------------------------------
228
229 -(void) sendDelegateMessage: (FNMessage)message forSubscription: (FNSubscriptionRef)subscription
230 {
231 NSValue* subValue = [NSValue valueWithPointer: subscription];
232 int idx = [subscribedObjects indexOfObject: subValue];
233 if( idx == NSNotFound )
234 {
235 NSLog( @"Notification for unknown subscription." );
236 return;
237 }
238 NSString* path = [subscribedPaths objectAtIndex: idx];
239
240 [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName: UKFileWatcherWriteNotification
241 object: self
242 userInfo: [NSDictionary dictionaryWithObjectsAndKeys: path, @"path", nil]];
243
244 [delegate watcher: self receivedNotification: UKFileWatcherWriteNotification forPath: path];
245 //NSLog( @"UKFNSubscribeFileWatcher noticed change to %@", path ); // DEBUG ONLY!
246 }
247
248 @end
249
250
251
252 // -----------------------------------------------------------------------------
253 // UKFileSubscriptionProc:
254 // Callback function we hand to Carbon so it can tell us when something
255 // changed about our watched folders. We set the refcon to a pointer to
256 // our object. This simply extracts the object and hands the info off to
257 // sendDelegateMessage:forSubscription: which does the actual work.
258 // -----------------------------------------------------------------------------
259
260 void UKFileSubscriptionProc( FNMessage message, OptionBits flags, void *refcon, FNSubscriptionRef subscription )
261 {
262 UKFNSubscribeFileWatcher* obj = (UKFNSubscribeFileWatcher*) refcon;
263
264 if( message == kFNDirectoryModifiedMessage ) // No others exist as of 10.4
265 [obj sendDelegateMessage: message forSubscription: subscription];
266 else
267 NSLog( @"UKFileSubscriptionProc: Unknown message %d", message );
268 }
Something went wrong with that request. Please try again.