Tagging the 1.1 (68) release of XspfQT project.
@@ -0,0 +1,258 @@ | ||
1 | +// | |
2 | +// XspfTrackList.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfTrackList.h" | |
10 | + | |
11 | + | |
12 | +@implementation XspfTrackList | |
13 | +- (id)initWithXMLElement:(NSXMLElement *)element | |
14 | +{ | |
15 | + self = [super init]; | |
16 | + | |
17 | + NSArray *elems = [element elementsForName:@"title"]; | |
18 | + if(elems && [elems count] != 0) { | |
19 | + NSString *t = [[elems objectAtIndex:0] stringValue]; | |
20 | + [self setTitle:t]; | |
21 | + } | |
22 | + | |
23 | + elems = [element elementsForName:@"track"]; | |
24 | + if(!elems) { | |
25 | + [self release]; | |
26 | + return nil; | |
27 | + } | |
28 | + tracks = [[NSMutableArray alloc] init]; | |
29 | + | |
30 | + unsigned i, count; | |
31 | + for(i = 0, count = [elems count]; i < count; i++) { | |
32 | + NSXMLElement *trackElem = [elems objectAtIndex:i]; | |
33 | + XspfComponent *track = [XspfComponent xspfComponemtWithXMLElement:trackElem]; | |
34 | + if(track) { | |
35 | + [self addChild:track]; | |
36 | + } | |
37 | + } | |
38 | + [self setCurrentIndex:NSNotFound]; | |
39 | + | |
40 | + return self; | |
41 | +} | |
42 | +- (void)dealloc | |
43 | +{ | |
44 | + [self setCurrentIndex:NSNotFound]; | |
45 | + | |
46 | + [tracks release]; | |
47 | + | |
48 | + [super dealloc]; | |
49 | +} | |
50 | +- (NSXMLElement *)XMLElement | |
51 | +{ | |
52 | + id node = [NSXMLElement elementWithName:@"trackList"]; | |
53 | + | |
54 | + id titleElem = [NSXMLElement elementWithName:@"title" stringValue:[self title]]; | |
55 | + if(titleElem) { | |
56 | + [node addChild:titleElem]; | |
57 | + } | |
58 | + | |
59 | + NSEnumerator *tracksEnum = [tracks objectEnumerator]; | |
60 | + id n; | |
61 | + while(n = [tracksEnum nextObject]) { | |
62 | + [node addChild:[n XMLElement]]; | |
63 | + } | |
64 | + | |
65 | + return node; | |
66 | +} | |
67 | +- (void)observeValueForKeyPath:(NSString *)keyPath | |
68 | + ofObject:(id)object | |
69 | + change:(NSDictionary *)change | |
70 | + context:(void *)context | |
71 | +{ | |
72 | + if([keyPath isEqualToString:@"isPlayed"]) { | |
73 | +// NSLog(@"Observe key path(%@).", keyPath); | |
74 | + [self willChangeValueForKey:@"isPlayed"]; | |
75 | + [self didChangeValueForKey:@"isPlayed"]; | |
76 | + return; | |
77 | + } | |
78 | + | |
79 | + [super observeValueForKeyPath:keyPath | |
80 | + ofObject:object | |
81 | + change:change | |
82 | + context:context]; | |
83 | +} | |
84 | + | |
85 | +// this mothod not check toTrack and fromTrack are in tracks array. | |
86 | +// Do not call directly. | |
87 | +- (void)changeObserveFrom:(XspfComponent *)fromTrack to:(XspfComponent *)toTrack | |
88 | +{ | |
89 | + if(fromTrack == toTrack) return; | |
90 | + | |
91 | + @try { | |
92 | + [toTrack addObserver:self | |
93 | + forKeyPath:@"isPlayed" | |
94 | + options:NSKeyValueObservingOptionNew | |
95 | + context:NULL]; | |
96 | + [fromTrack removeObserver:self forKeyPath:@"isPlayed"]; | |
97 | + } | |
98 | + @catch (id ex) { | |
99 | +// NSLog(@"Caught exception(%@).",ex); | |
100 | + if(![[ex name] isEqualToString:NSRangeException]) { | |
101 | + NSLog(@"Exception ### named %@", [ex name]); | |
102 | + @throw; | |
103 | + } | |
104 | + } | |
105 | + @finally { | |
106 | +// NSLog(@"Prev -> %@\nNew -> %@", fromTrack, toTrack); | |
107 | + [self willChangeValueForKey:@"isPlayed"]; | |
108 | + [fromTrack deselect]; | |
109 | + [toTrack select]; | |
110 | + [self didChangeValueForKey:@"isPlayed"]; | |
111 | + } | |
112 | +} | |
113 | + | |
114 | +- (void)setSelectionIndex:(unsigned)index | |
115 | +{ | |
116 | + [self setCurrentIndex:index]; | |
117 | +} | |
118 | +- (void)setCurrentIndex:(unsigned)index | |
119 | +{ | |
120 | + unsigned prev; | |
121 | + | |
122 | + if(index < 0) return; | |
123 | + if([tracks count] <= index && index != NSNotFound) return; | |
124 | + | |
125 | + [self willChangeValueForKey:@"qtMovie"]; | |
126 | + [self willChangeValueForKey:@"currentTrack"]; | |
127 | + prev = currentIndex; | |
128 | + currentIndex = index; | |
129 | + [self didChangeValueForKey:@"currentTrack"]; | |
130 | + [self didChangeValueForKey:@"qtMovie"]; | |
131 | + | |
132 | + XspfComponent *t= nil; | |
133 | + @try { | |
134 | + t = [tracks objectAtIndex:prev]; | |
135 | + } | |
136 | + @catch (id ex) { | |
137 | + if(![[ex name] isEqualToString:NSRangeException]) { | |
138 | + NSLog(@"Exception ### named %@", [ex name]); | |
139 | + @throw; | |
140 | + } | |
141 | + } | |
142 | + XspfComponent *t2 = [self currentTrack]; | |
143 | + | |
144 | + [self changeObserveFrom:t to:t2]; | |
145 | +} | |
146 | +- (unsigned)currentIndex | |
147 | +{ | |
148 | + return currentIndex; | |
149 | +} | |
150 | + | |
151 | +- (void)next | |
152 | +{ | |
153 | + [self setCurrentIndex:[self currentIndex] + 1]; | |
154 | +} | |
155 | +- (void)previous | |
156 | +{ | |
157 | + [self setCurrentIndex:[self currentIndex] - 1]; | |
158 | +} | |
159 | +- (NSArray *)children | |
160 | +{ | |
161 | + return tracks; | |
162 | +} | |
163 | +- (BOOL)isLeaf | |
164 | +{ | |
165 | + return NO; | |
166 | +} | |
167 | + | |
168 | +// primitive. | |
169 | +- (void)insertChild:(XspfComponent *)child atIndex:(unsigned)index | |
170 | +{ | |
171 | + if(!child) return; | |
172 | + if(![child isKindOfClass:[XspfComponent class]]) { | |
173 | + NSLog(@"addChild: argument class is MUST kind of XspfComponent. " | |
174 | + @"but argument class is %@<%p>.", | |
175 | + NSStringFromClass([child class]), child); | |
176 | + return; | |
177 | + } | |
178 | + [tracks insertObject:child atIndex:index]; | |
179 | + [child setParent:self]; | |
180 | +} | |
181 | +// primitive. | |
182 | +- (void)removeChild:(XspfComponent *)child | |
183 | +{ | |
184 | + if(!child) return; | |
185 | + if(![tracks containsObject:child]) return; | |
186 | + | |
187 | + NSUInteger index = [tracks indexOfObject:child]; | |
188 | + BOOL isSelectedItem = [child isSelected]; | |
189 | + BOOL mustChangeSelection = NO; | |
190 | + | |
191 | + if(index <= currentIndex) { | |
192 | + mustChangeSelection = YES; | |
193 | + } | |
194 | + | |
195 | + [self willChangeValueForKey:@"children"]; | |
196 | + [[child retain] autorelease]; | |
197 | + [child setParent:nil]; | |
198 | + [tracks removeObject:child]; | |
199 | + [self didChangeValueForKey:@"children"]; | |
200 | + | |
201 | + if(mustChangeSelection) { | |
202 | + // ### CAUTION ### | |
203 | + // this line directly change currentIndex. | |
204 | + [self willChangeValueForKey:@"qtMovie"]; | |
205 | + [self willChangeValueForKey:@"currentTrack"]; | |
206 | + currentIndex--; | |
207 | + [self didChangeValueForKey:@"currentTrack"]; | |
208 | + [self didChangeValueForKey:@"qtMovie"]; | |
209 | + | |
210 | + id newSelection = nil; | |
211 | + id oldSelection = nil; | |
212 | + if(isSelectedItem) { | |
213 | + oldSelection = child; | |
214 | + newSelection = [self currentTrack]; | |
215 | + } | |
216 | + [self changeObserveFrom:oldSelection to:newSelection]; | |
217 | + } | |
218 | +} | |
219 | + | |
220 | +- (void)addChild:(XspfComponent *)child | |
221 | +{ | |
222 | + unsigned num = [tracks count]; | |
223 | + [self insertChild:child atIndex:num]; | |
224 | +} | |
225 | +- (void)removeChildAtIndex:(unsigned)index | |
226 | +{ | |
227 | + id child = [tracks objectAtIndex:index]; | |
228 | + [self removeChild:child]; | |
229 | +} | |
230 | + | |
231 | +- (NSString *)description | |
232 | +{ | |
233 | + return [tracks description]; | |
234 | +} | |
235 | + | |
236 | +- (XspfComponent *)currentTrack | |
237 | +{ | |
238 | + if([tracks count] > currentIndex) { | |
239 | + return [tracks objectAtIndex:currentIndex]; | |
240 | + } | |
241 | + return nil; | |
242 | +} | |
243 | + | |
244 | +- (QTMovie *)qtMovie | |
245 | +{ | |
246 | + return [[self currentTrack] qtMovie]; | |
247 | +} | |
248 | + | |
249 | +- (void)setIsPlayed:(BOOL)state {} | |
250 | +- (BOOL)isPlayed | |
251 | +{ | |
252 | + XspfComponent *t = [self currentTrack]; | |
253 | + if(t) { | |
254 | + return [t isPlayed]; | |
255 | + } | |
256 | + return NO; | |
257 | +} | |
258 | +@end |
@@ -0,0 +1,57 @@ | ||
1 | +// encoding=utf-8 | |
2 | +PRODUCT_NAME=XspfQT | |
3 | +PRODUCT_EXTENSION=app | |
4 | +BUILD_PATH=./build | |
5 | +DEPLOYMENT=Release | |
6 | +APP_BUNDLE=$(PRODUCT_NAME).$(PRODUCT_EXTENSION) | |
7 | +APP=$(BUILD_PATH)/$(DEPLOYMENT)/$(APP_BUNDLE) | |
8 | +APP_NAME=$(BUILD_PATH)/$(DEPLOYMENT)/$(PRODUCT_NAME) | |
9 | +INFO_PLIST=Info.plist | |
10 | + | |
11 | +URL_XspfQT = svn+ssh://macmini/usr/local/svnrepos/XspfQT | |
12 | +HEAD = $(URL_XspfQT)/XspfQT | |
13 | +TAGS_DIR = $(URL_XspfQT)/tags | |
14 | + | |
15 | +VER_CMD=grep -A1 'CFBundleShortVersionString' $(INFO_PLIST) | tail -1 | tr -d "'\t</string>" | |
16 | +VERSION=$(shell $(VER_CMD)) | |
17 | + | |
18 | +all: | |
19 | + @echo do nothig. | |
20 | + @echo use target tagging | |
21 | + | |
22 | +tagging: update_svn | |
23 | + @echo "Tagging the $(VERSION) (x) release of XspfQT project." | |
24 | + @echo "" | |
25 | + @REV=`LC_ALL=C svn info | awk '/Last Changed Rev/ {print $$4}'` ; \ | |
26 | + echo svn copy $(HEAD) $(TAGS_DIR)/release-$(VERSION).$${REV} | |
27 | + | |
28 | +Localizable: BSTRADocument.m | |
29 | + genstrings -o English.lproj $< | |
30 | + (cd English.lproj; ${MAKE} $@;) | |
31 | + genstrings -o Japanese.lproj $< | |
32 | + (cd Japanese.lproj; ${MAKE} $@;) | |
33 | + | |
34 | +checkLocalizable: | |
35 | + (cd English.lproj; ${MAKE} $@;) | |
36 | + (cd Japanese.lproj; ${MAKE} $@;) | |
37 | + | |
38 | +release: updateRevision | |
39 | + xcodebuild -configuration $(DEPLOYMENT) | |
40 | + $(MAKE) restorInfoPlist | |
41 | + | |
42 | +package: release | |
43 | + REV=`LC_ALL=C svn info | awk '/Last Changed Rev/ {print $$4}'`; \ | |
44 | + ditto -ck -rsrc --keepParent $(APP) $(APP_NAME)-$(VERSION)-$${REV}.zip | |
45 | + | |
46 | +updateRevision: update_svn | |
47 | + if [ ! -f $(INFO_PLIST).bak ] ; then cp $(INFO_PLIST) $(INFO_PLIST).bak ; fi ; \ | |
48 | + REV=`LC_ALL=C svn info | awk '/Last Changed Rev/ {print $$4}'` ; \ | |
49 | + sed -e "s/%%%%REVISION%%%%/$${REV}/" $(INFO_PLIST) > $(INFO_PLIST).r ; \ | |
50 | + mv -f $(INFO_PLIST).r $(INFO_PLIST) ; \ | |
51 | + | |
52 | +restorInfoPlist: | |
53 | + if [ -f $(INFO_PLIST).bak ] ; then mv -f $(INFO_PLIST).bak $(INFO_PLIST) ; fi | |
54 | + | |
55 | +update_svn: | |
56 | + svn up | |
57 | + |
@@ -0,0 +1,236 @@ | ||
1 | +// | |
2 | +// XspfTrack.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfTrack.h" | |
10 | + | |
11 | +@interface XspfTrack (Private) | |
12 | +- (void)setSavedDateWithQTTime:(QTTime)qttime; | |
13 | +- (NSDate *)duration; | |
14 | +@end | |
15 | + | |
16 | +@implementation XspfTrack | |
17 | +- (id)initWithXMLElement:(NSXMLElement *)element | |
18 | +{ | |
19 | + self = [super init]; | |
20 | + | |
21 | + NSArray *elems = [element elementsForName:@"location"]; | |
22 | + if(!elems || [elems count] == 0) { | |
23 | + [self release]; | |
24 | + return nil; | |
25 | + } | |
26 | + | |
27 | + NSString *loc = [[elems objectAtIndex:0] stringValue];; | |
28 | + [self setLocationString:loc]; | |
29 | + | |
30 | + NSString *t; | |
31 | + elems = [element elementsForName:@"title"]; | |
32 | + if(!elems || [elems count] == 0) { | |
33 | + t = [[self locationString] lastPathComponent]; | |
34 | + } else { | |
35 | + t = [[elems objectAtIndex:0] stringValue]; | |
36 | + } | |
37 | + [self setTitle:t]; | |
38 | + | |
39 | + elems = [element elementsForName:@"duration"]; | |
40 | + if(elems && [elems count] != 0) { | |
41 | + t = [[elems objectAtIndex:0] stringValue]; | |
42 | + NSTimeInterval ti = [t doubleValue] / 1000; | |
43 | + QTTime q = QTMakeTimeWithTimeInterval(ti); | |
44 | + [self setSavedDateWithQTTime:q]; | |
45 | + } | |
46 | + | |
47 | + return self; | |
48 | +} | |
49 | +- (void)dealloc | |
50 | +{ | |
51 | + [location release]; | |
52 | + [movie release]; | |
53 | + [savedDate release]; | |
54 | + | |
55 | + [super dealloc]; | |
56 | +} | |
57 | +- (NSXMLElement *)XMLElement | |
58 | +{ | |
59 | + id node = [NSXMLElement elementWithName:@"track"]; | |
60 | + | |
61 | + id locElem = [NSXMLElement elementWithName:@"location" stringValue:[self locationString]]; | |
62 | + if(locElem) { | |
63 | + [node addChild:locElem]; | |
64 | + } | |
65 | + id titleElem = [NSXMLElement elementWithName:@"title" stringValue:[self title]]; | |
66 | + if(titleElem) { | |
67 | + [node addChild:titleElem]; | |
68 | + } | |
69 | + | |
70 | + id d = [self duration]; | |
71 | + if(d) { | |
72 | + NSTimeInterval t = [d timeIntervalSince1970]; | |
73 | + t += [[NSTimeZone systemTimeZone] secondsFromGMT]; | |
74 | + unsigned long long scaledT = (unsigned long long)t; | |
75 | + scaledT *= 1000; | |
76 | + id durationElem = [NSXMLElement elementWithName:@"duration" | |
77 | + stringValue:[NSString stringWithFormat:@"%qu", scaledT]]; | |
78 | + if(durationElem) { | |
79 | + [node addChild:durationElem]; | |
80 | + } | |
81 | + } | |
82 | + | |
83 | + return node; | |
84 | +} | |
85 | +- (void)setLocation:(NSURL *)loc | |
86 | +{ | |
87 | + if(location && ![location isKindOfClass:[NSURL class]]) return; | |
88 | + if(location == loc) return; | |
89 | + if([location isEqualTo:loc]) return; | |
90 | + | |
91 | + [location autorelease]; | |
92 | + location = [loc retain]; | |
93 | +} | |
94 | +- (NSURL *)location | |
95 | +{ | |
96 | + return location; | |
97 | +} | |
98 | +- (void)setLocationString:(NSString *)loc | |
99 | +{ | |
100 | + [self setLocation:[NSURL URLWithString:loc]]; | |
101 | +} | |
102 | +- (NSString *)locationString | |
103 | +{ | |
104 | + return [[self location] absoluteString]; | |
105 | +} | |
106 | + | |
107 | +- (void)setSavedDateWithQTTime:(QTTime)qttime | |
108 | +{ | |
109 | + id t = [NSValueTransformer valueTransformerForName:@"XspfQTTimeDateTransformer"]; | |
110 | + savedDate = [[t transformedValue:[NSValue valueWithQTTime:qttime]] retain]; | |
111 | +} | |
112 | +- (NSDate *)savedDate | |
113 | +{ | |
114 | + return savedDate; | |
115 | +} | |
116 | +- (NSDate *)duration | |
117 | +{ | |
118 | + if(savedDate) return savedDate; | |
119 | + | |
120 | + if(!movie) return nil; | |
121 | + | |
122 | + [self setSavedDateWithQTTime:[movie duration]]; | |
123 | + return [self savedDate]; | |
124 | +} | |
125 | +- (QTMovie *)qtMovie | |
126 | +{ | |
127 | + if(movie) { | |
128 | + [[self class] cancelPreviousPerformRequestsWithTarget:self]; | |
129 | + return movie; | |
130 | + } | |
131 | + if(![QTMovie canInitWithURL:[self location]]) return nil; | |
132 | + | |
133 | + NSError *error = nil; | |
134 | + NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys: | |
135 | + [self location], QTMovieURLAttribute, | |
136 | + [NSNumber numberWithBool:NO], QTMovieOpenAsyncOKAttribute, | |
137 | + nil]; | |
138 | + movie = [[QTMovie alloc] initWithAttributes:attrs error:&error]; | |
139 | +// movie = [[QTMovie alloc] initWithURL:[self location] error:&error]; | |
140 | + | |
141 | + { | |
142 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
143 | +// [nc addObserver:self | |
144 | +// selector:@selector(notifee:) | |
145 | +// name:@"QTMoviePrerollCompleteNotification" | |
146 | +// object:movie]; | |
147 | + [nc addObserver:self | |
148 | + selector:@selector(notifee:) | |
149 | + name:QTMovieRateDidChangeNotification | |
150 | + object:movie]; | |
151 | +// [nc addObserver:self | |
152 | +// selector:@selector(notifee:) | |
153 | +// name:@"QTMovieDidEndNotification" | |
154 | +// object:movie]; | |
155 | + } | |
156 | + | |
157 | + | |
158 | + [self willChangeValueForKey:@"duration"]; | |
159 | + [self didChangeValueForKey:@"duration"]; | |
160 | + | |
161 | + return movie; | |
162 | +} | |
163 | +- (void)setIsPlayed:(BOOL)state | |
164 | +{ | |
165 | + isPlayed = state; | |
166 | +} | |
167 | +- (BOOL)isPlayed | |
168 | +{ | |
169 | + return isPlayed; | |
170 | +} | |
171 | +- (void)next | |
172 | +{ | |
173 | + [[self parent] next]; | |
174 | +} | |
175 | +- (void)previous | |
176 | +{ | |
177 | + [[self parent] previous]; | |
178 | +} | |
179 | + | |
180 | +- (void)deselect | |
181 | +{ | |
182 | + [movie stop]; | |
183 | + [self performSelector:@selector(purgeQTMovie) | |
184 | + withObject:nil | |
185 | + afterDelay:4.5]; | |
186 | +// [self setIsPlayed:NO]; | |
187 | + [super deselect]; | |
188 | +} | |
189 | +- (void)purgeQTMovie | |
190 | +{ | |
191 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
192 | + [nc removeObserver:self | |
193 | + name:nil | |
194 | + object:movie]; | |
195 | + | |
196 | +// [movie invalidate]; | |
197 | +// NSLog(@"Purge! retain count is %u", [movie retainCount]); | |
198 | + | |
199 | + [movie release]; | |
200 | + movie = nil; | |
201 | +} | |
202 | + | |
203 | +- (void)notifee:(id)notification | |
204 | +{ | |
205 | +// NSLog(@"Notifed: name -> (%@)\ndict -> (%@)", [notification name], [notification userInfo]); | |
206 | + | |
207 | + NSNumber *rateValue = [[notification userInfo] objectForKey:QTMovieRateDidChangeNotificationParameter]; | |
208 | + if(rateValue) { | |
209 | + float rate = [rateValue floatValue]; | |
210 | + if(rate == 0) { | |
211 | + [self setIsPlayed:NO]; | |
212 | + } else if(rate == 1) { | |
213 | + [self setIsPlayed:YES]; | |
214 | + } | |
215 | + } | |
216 | +} | |
217 | + | |
218 | +- (NSUInteger)hash | |
219 | +{ | |
220 | + return [location hash]; | |
221 | +} | |
222 | +- (BOOL)isEqual:(XspfTrack *)other | |
223 | +{ | |
224 | + if(![other isMemberOfClass:[XspfTrack class]]) return NO; | |
225 | + if(![[other location] isEqual:location]) return NO; | |
226 | + if(![[other title] isEqualToString:[self title]]) return NO; | |
227 | + | |
228 | + return YES; | |
229 | +} | |
230 | + | |
231 | +- (NSString *)description | |
232 | +{ | |
233 | + return [NSString stringWithFormat:@"Title:(%@)\nLocation:(%@)", | |
234 | + [self title], [self location]]; | |
235 | +} | |
236 | +@end |
@@ -0,0 +1,431 @@ | ||
1 | +// | |
2 | +// XspfMovieWindowController.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfMovieWindowController.h" | |
10 | +#import "XspfDocument.h" | |
11 | +#import "XspfComponent.h" | |
12 | +#import "XspfFullScreenWindow.h" | |
13 | + | |
14 | + | |
15 | +@interface XspfMovieWindowController (Private) | |
16 | +- (NSSize)windowSizeWithoutQTView; | |
17 | +- (void)sizeTofitWidnow; | |
18 | +- (NSSize)fitSizeToSize:(NSSize)toSize; | |
19 | +- (NSWindow *)fullscreenWindow; | |
20 | +@end | |
21 | + | |
22 | +@implementation XspfMovieWindowController | |
23 | + | |
24 | +#pragma mark ### Static variables ### | |
25 | +static const float sVolumeDelta = 0.2; | |
26 | +static NSString *const kQTMovieKeyPath = @"trackList.qtMovie"; | |
27 | +static NSString *const kIsPlayedKeyPath = @"trackList.isPlayed"; | |
28 | + | |
29 | +- (id)init | |
30 | +{ | |
31 | + if(self = [super initWithWindowNibName:@"XspfDocument"]) { | |
32 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
33 | + [nc addObserver:self | |
34 | + selector:@selector(applicationWillTerminate:) | |
35 | + name:NSApplicationWillTerminateNotification | |
36 | + object:NSApp]; | |
37 | + | |
38 | + updateTime = [NSTimer scheduledTimerWithTimeInterval:0.3 | |
39 | + target:self | |
40 | + selector:@selector(updateTimeIfNeeded:) | |
41 | + userInfo:NULL | |
42 | + repeats:YES]; | |
43 | + } | |
44 | + | |
45 | + return self; | |
46 | +} | |
47 | + | |
48 | +- (void)dealloc | |
49 | +{ | |
50 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
51 | + [nc removeObserver:self]; | |
52 | + | |
53 | + [self setQtMovie:nil]; | |
54 | + | |
55 | + [fullscreenWindow release]; | |
56 | + [updateTime invalidate]; | |
57 | + [prevMouseMovedDate release]; | |
58 | + | |
59 | + [super dealloc]; | |
60 | +} | |
61 | +- (void)awakeFromNib | |
62 | +{ | |
63 | + prevMouseMovedDate = [[NSDate dateWithTimeIntervalSinceNow:0.0] retain]; | |
64 | + | |
65 | + id d = [self document]; | |
66 | +// NSLog(@"Add Observed! %@", d); | |
67 | + [d addObserver:self | |
68 | + forKeyPath:kQTMovieKeyPath | |
69 | + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | |
70 | + context:NULL]; | |
71 | + [d addObserver:self | |
72 | + forKeyPath:kIsPlayedKeyPath | |
73 | + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | |
74 | + context:NULL]; | |
75 | + | |
76 | + [self setValue:[NSNumber numberWithInt:0] | |
77 | + forKeyPath:@"document.trackList.selectionIndex"]; | |
78 | + [self sizeTofitWidnow]; | |
79 | + [self play]; | |
80 | +} | |
81 | + | |
82 | +- (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName | |
83 | +{ | |
84 | + id title = [self valueForKeyPath:@"document.trackList.currentTrack.title"]; | |
85 | + if(title) { | |
86 | + return [NSString stringWithFormat:@"%@ - %@", | |
87 | + displayName, title]; | |
88 | + } | |
89 | + return displayName; | |
90 | +} | |
91 | + | |
92 | +#pragma mark ### KVO & KVC ### | |
93 | +- (void)observeValueForKeyPath:(NSString *)keyPath | |
94 | + ofObject:(id)object | |
95 | + change:(NSDictionary *)change | |
96 | + context:(void *)context | |
97 | +{ | |
98 | +// NSLog(@"Observed!"); | |
99 | + if([keyPath isEqualToString:kQTMovieKeyPath]) { | |
100 | + id new = [change objectForKey:NSKeyValueChangeNewKey]; | |
101 | + [self setQtMovie:new]; | |
102 | + return; | |
103 | + } | |
104 | + if([keyPath isEqualToString:kIsPlayedKeyPath]) { | |
105 | + id new = [change objectForKey:NSKeyValueChangeNewKey]; | |
106 | +// NSLog(@"Observed!"); | |
107 | + if([new boolValue]) { | |
108 | + [playButton setTitle:@"||"]; | |
109 | + } else { | |
110 | + [playButton setTitle:@">"]; | |
111 | + } | |
112 | + return; | |
113 | + } | |
114 | + | |
115 | + | |
116 | + [super observeValueForKeyPath:keyPath | |
117 | + ofObject:object | |
118 | + change:change | |
119 | + context:context]; | |
120 | +} | |
121 | + | |
122 | +- (void)setQtMovie:(QTMovie *)qt | |
123 | +{ | |
124 | + if(qtMovie == qt) return; | |
125 | + if([qtMovie isEqual:qt]) return; | |
126 | + if(qt == (id)[NSNull null]) qt = nil; | |
127 | + | |
128 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
129 | + | |
130 | + if(qtMovie) { | |
131 | + [nc removeObserver:self name:nil object:qtMovie]; | |
132 | + } | |
133 | + if(qt) { | |
134 | + [nc addObserver:self selector:@selector(didEndMovie:) name:QTMovieDidEndNotification object:qt]; | |
135 | + } | |
136 | + | |
137 | + if(qtMovie) { | |
138 | + [qt setVolume:[qtMovie volume]]; | |
139 | + [qt setMuted:[qtMovie muted]]; | |
140 | + } | |
141 | + [qtMovie autorelease]; | |
142 | + qtMovie = [qt retain]; | |
143 | + | |
144 | + if(!qtMovie) return; | |
145 | + | |
146 | + [self synchronizeWindowTitleWithDocumentName]; | |
147 | + [self sizeTofitWidnow]; | |
148 | + [self play]; | |
149 | +} | |
150 | +- (QTMovie *)qtMovie | |
151 | +{ | |
152 | + return qtMovie; | |
153 | +} | |
154 | + | |
155 | +#pragma mark ### Other functions ### | |
156 | +- (NSSize)windowSizeWithoutQTView | |
157 | +{ | |
158 | + if(windowSizeWithoutQTView.width == 0 | |
159 | + && windowSizeWithoutQTView.height == 0) { | |
160 | + QTMovie *curMovie = [self qtMovie]; | |
161 | + if(!curMovie) return windowSizeWithoutQTView; | |
162 | + | |
163 | + NSSize qtViewSize = [qtView frame].size; | |
164 | + NSSize currentWindowSize = [[self window] frame].size; | |
165 | + | |
166 | + windowSizeWithoutQTView = NSMakeSize(currentWindowSize.width - qtViewSize.width, | |
167 | + currentWindowSize.height - qtViewSize.height); | |
168 | + } | |
169 | + | |
170 | + return windowSizeWithoutQTView; | |
171 | +} | |
172 | +- (void)sizeTofitWidnow | |
173 | +{ | |
174 | + id window = [self window]; | |
175 | + NSRect frame = [window frame]; | |
176 | + NSSize newSize = [self fitSizeToSize:frame.size]; | |
177 | + frame.origin.y += frame.size.height - newSize.height; | |
178 | + frame.size = newSize; | |
179 | + | |
180 | + [window setFrame:frame display:YES animate:YES]; | |
181 | +} | |
182 | +- (NSSize)fitSizeToSize:(NSSize)toSize | |
183 | +{ | |
184 | + QTMovie *curMovie = [self qtMovie]; | |
185 | + if(!curMovie) return toSize; | |
186 | + | |
187 | + // Area size without QTMovieView. | |
188 | + NSSize delta = [self windowSizeWithoutQTView]; | |
189 | + | |
190 | + NSSize movieSize = [[curMovie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; | |
191 | + | |
192 | + float targetViewWidth = toSize.width - delta.width; | |
193 | + float targetViewHeight = targetViewWidth * (movieSize.height / movieSize.width); | |
194 | + | |
195 | + targetViewWidth += delta.width; | |
196 | + targetViewHeight += delta.height; | |
197 | + | |
198 | + NSSize newSize = NSMakeSize(targetViewWidth, targetViewHeight); | |
199 | + | |
200 | + return newSize; | |
201 | +} | |
202 | + | |
203 | +- (void)play | |
204 | +{ | |
205 | + [qtView performSelectorOnMainThread:@selector(play:) withObject:self waitUntilDone:NO]; | |
206 | +} | |
207 | +- (void)pause | |
208 | +{ | |
209 | + [qtView performSelectorOnMainThread:@selector(pause:) withObject:self waitUntilDone:NO]; | |
210 | +} | |
211 | +- (void)stop | |
212 | +{ | |
213 | + [qtView performSelectorOnMainThread:@selector(pause:) withObject:self waitUntilDone:YES]; | |
214 | +} | |
215 | +- (void)enterFullScreen | |
216 | +{ | |
217 | + NSWindow *w = [self fullscreenWindow]; | |
218 | + | |
219 | + nomalModeSavedFrame = [qtView frame]; | |
220 | + | |
221 | + [[self window] orderOut:self]; | |
222 | + [w setContentView:qtView]; | |
223 | + | |
224 | +// [NSMenu setMenuBarVisible:NO]; | |
225 | + SetSystemUIMode (kUIModeAllHidden, kUIOptionAutoShowMenuBar); | |
226 | + | |
227 | + [w makeKeyAndOrderFront:self]; | |
228 | + [w makeFirstResponder:qtView]; | |
229 | +} | |
230 | +- (void)exitFullScreen | |
231 | +{ | |
232 | + NSWindow *w = [self fullscreenWindow]; | |
233 | + | |
234 | + [qtView retain]; | |
235 | + { | |
236 | + [qtView removeFromSuperview]; | |
237 | + [qtView setFrame:nomalModeSavedFrame]; | |
238 | + [[[self window] contentView] addSubview:qtView]; | |
239 | + } | |
240 | + [qtView release]; | |
241 | + | |
242 | + [NSMenu setMenuBarVisible:YES]; | |
243 | + [w orderOut:self]; | |
244 | + [[self window] makeKeyAndOrderFront:self]; | |
245 | + [[self window] makeFirstResponder:qtView]; | |
246 | +} | |
247 | + | |
248 | +- (NSWindow *)fullscreenWindow | |
249 | +{ | |
250 | + if(fullscreenWindow) return fullscreenWindow; | |
251 | + | |
252 | + NSRect mainScreenRect = [[NSScreen mainScreen] frame]; | |
253 | + fullscreenWindow = [[XspfFullScreenWindow alloc] initWithContentRect:mainScreenRect | |
254 | + styleMask:NSBorderlessWindowMask | |
255 | + backing:NSBackingStoreBuffered | |
256 | + defer:YES]; | |
257 | + [fullscreenWindow setReleasedWhenClosed:NO]; | |
258 | + [fullscreenWindow setBackgroundColor:[NSColor blackColor]]; | |
259 | + [fullscreenWindow setDelegate:self]; | |
260 | + [fullscreenWindow setWindowController:self]; | |
261 | + | |
262 | + return fullscreenWindow; | |
263 | +} | |
264 | + | |
265 | +#pragma mark ### Actions ### | |
266 | +- (IBAction)togglePlayAndPause:(id)sender | |
267 | +{ | |
268 | + if([[self valueForKeyPath:@"document.trackList.isPlayed"] boolValue]) { | |
269 | + [self pause]; | |
270 | + } else { | |
271 | + [self play]; | |
272 | + } | |
273 | +} | |
274 | + | |
275 | +- (IBAction)turnUpVolume:(id)sender | |
276 | +{ | |
277 | + NSNumber *cv = [self valueForKeyPath:@"qtMovie.volume"]; | |
278 | + cv = [NSNumber numberWithFloat:[cv floatValue] + sVolumeDelta]; | |
279 | + [self setValue:cv forKeyPath:@"qtMovie.volume"]; | |
280 | +} | |
281 | +- (IBAction)turnDownVolume:(id)sender | |
282 | +{ | |
283 | + NSNumber *cv = [self valueForKeyPath:@"qtMovie.volume"]; | |
284 | + cv = [NSNumber numberWithFloat:[cv floatValue] - sVolumeDelta]; | |
285 | + [self setValue:cv forKeyPath:@"qtMovie.volume"]; | |
286 | +} | |
287 | +- (IBAction)toggleFullScreenMode:(id)sender | |
288 | +{ | |
289 | + if(fullScreenMode) { | |
290 | + [self exitFullScreen]; | |
291 | + fullScreenMode = NO; | |
292 | + } else { | |
293 | + [self enterFullScreen]; | |
294 | + fullScreenMode = YES; | |
295 | + } | |
296 | +} | |
297 | + | |
298 | +- (IBAction)forwardTagValueSecends:(id)sender | |
299 | +{ | |
300 | + if(![sender respondsToSelector:@selector(tag)]) return; | |
301 | + | |
302 | + int tag = [sender tag]; | |
303 | + if(tag == 0) return; | |
304 | + | |
305 | + QTTime current = [[self qtMovie] currentTime]; | |
306 | + NSTimeInterval cur; | |
307 | + if(!QTGetTimeInterval(current, &cur)) return; | |
308 | + | |
309 | + QTTime new = QTMakeTimeWithTimeInterval(cur + tag); | |
310 | + [[self qtMovie] setCurrentTime:new]; | |
311 | +} | |
312 | +- (IBAction)backwardTagValueSecends:(id)sender | |
313 | +{ | |
314 | + if(![sender respondsToSelector:@selector(tag)]) return; | |
315 | + | |
316 | + int tag = [sender tag]; | |
317 | + if(tag == 0) return; | |
318 | + | |
319 | + QTTime current = [[self qtMovie] currentTime]; | |
320 | + NSTimeInterval cur; | |
321 | + if(!QTGetTimeInterval(current, &cur)) return; | |
322 | + | |
323 | + QTTime new = QTMakeTimeWithTimeInterval(cur - tag); | |
324 | + [[self qtMovie] setCurrentTime:new]; | |
325 | +} | |
326 | +- (IBAction)nextTrack:(id)sender | |
327 | +{ | |
328 | + [qtView pause:sender]; | |
329 | + [[[self document] trackList] next]; | |
330 | +} | |
331 | +- (IBAction)previousTrack:(id)sender | |
332 | +{ | |
333 | + [qtView pause:sender]; | |
334 | + [[[self document] trackList] previous]; | |
335 | +} | |
336 | + | |
337 | +#pragma mark ### Notification & Timer ### | |
338 | +- (void)didEndMovie:(id)notification | |
339 | +{ | |
340 | + [[[self document] trackList] next]; | |
341 | +} | |
342 | +- (void)updateTimeIfNeeded:(id)timer | |
343 | +{ | |
344 | + QTMovie *qt = [self qtMovie]; | |
345 | + if(qt) { | |
346 | + // force update time indicator. | |
347 | + [qt willChangeValueForKey:@"currentTime"]; | |
348 | + [qt didChangeValueForKey:@"currentTime"]; | |
349 | + } | |
350 | + | |
351 | + // Hide cursor and controller, if mouse didn't move for 3 seconds. | |
352 | + NSPoint mouse = [NSEvent mouseLocation]; | |
353 | + if(!NSEqualPoints(prevMouse, mouse)) { | |
354 | + prevMouse = mouse; | |
355 | + [prevMouseMovedDate autorelease]; | |
356 | + prevMouseMovedDate = [[NSDate dateWithTimeIntervalSinceNow:0.0] retain]; | |
357 | + } else if(fullScreenMode && [prevMouseMovedDate timeIntervalSinceNow] < -3.0 ) { | |
358 | + [NSCursor setHiddenUntilMouseMoves:YES]; | |
359 | + // | |
360 | + // hide controller. | |
361 | + } | |
362 | +} | |
363 | + | |
364 | +#pragma mark ### NSResponder ### | |
365 | +- (void)cancelOperation:(id)sender | |
366 | +{ | |
367 | + if(fullScreenMode) { | |
368 | + [self toggleFullScreenMode:self]; | |
369 | + } | |
370 | +} | |
371 | + | |
372 | +#pragma mark ### NSMenu valivation ### | |
373 | +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem | |
374 | +{ | |
375 | + if([menuItem action] == @selector(toggleFullScreenMode:)) { | |
376 | + if(fullScreenMode) { | |
377 | + [menuItem setTitle:NSLocalizedString(@"Exit Full Screen", @"Exit Full Screen")]; | |
378 | + } else { | |
379 | + [menuItem setTitle:NSLocalizedString(@"Full Screen", @"Full Screen")]; | |
380 | + } | |
381 | + return YES; | |
382 | + } | |
383 | +// if([menuItem tag] == 10000) { | |
384 | +// NSString *title = [self valueForKeyPath:@"document.trackList.currentTrack.title"]; | |
385 | +// if(title) { | |
386 | +// [menuItem setTitle:title]; | |
387 | +// } | |
388 | +// return NO; | |
389 | +// } | |
390 | + | |
391 | + return YES; | |
392 | +} | |
393 | + | |
394 | +#pragma mark ### NSApplication Delegate ### | |
395 | +- (void)applicationWillTerminate:(NSNotification *)notification | |
396 | +{ | |
397 | + if(fullScreenMode) { | |
398 | + [self toggleFullScreenMode:self]; | |
399 | + } | |
400 | + [[self document] removeObserver:self forKeyPath:kQTMovieKeyPath]; | |
401 | + [[self document] removeObserver:self forKeyPath:kIsPlayedKeyPath]; | |
402 | +} | |
403 | + | |
404 | +#pragma mark ### NSWindow Delegate ### | |
405 | +- (BOOL)windowShouldClose:(id)sender | |
406 | +{ | |
407 | + [qtView pause:self]; | |
408 | + [self setQtMovie:nil]; | |
409 | + | |
410 | + [[self document] removeObserver:self forKeyPath:kQTMovieKeyPath]; | |
411 | + [[self document] removeObserver:self forKeyPath:kIsPlayedKeyPath]; | |
412 | + | |
413 | + [updateTime invalidate]; | |
414 | + updateTime = nil; | |
415 | + | |
416 | + return YES; | |
417 | +} | |
418 | +- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize | |
419 | +{ | |
420 | + return [self fitSizeToSize:frameSize]; | |
421 | +} | |
422 | +- (void)windowDidMove:(NSNotification *)notification | |
423 | +{ | |
424 | + if(fullscreenWindow && [notification object] == fullscreenWindow) { | |
425 | + NSRect r = [fullscreenWindow frame]; | |
426 | + if(!NSEqualPoints(r.origin, NSZeroPoint)) { | |
427 | + [fullscreenWindow setFrameOrigin:NSZeroPoint]; | |
428 | + } | |
429 | + } | |
430 | +} | |
431 | +@end |
@@ -0,0 +1,169 @@ | ||
1 | +// | |
2 | +// XspfQTInformationWindowController.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/09/14. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfQTInformationWindowController.h" | |
10 | +#import "XspfDocument.h" | |
11 | + | |
12 | + | |
13 | +@implementation XspfQTInformationWindowController | |
14 | +static XspfQTInformationWindowController *sharedInstance = nil; | |
15 | + | |
16 | ++ (XspfQTInformationWindowController *)sharedInstance | |
17 | +{ | |
18 | + @synchronized(self) { | |
19 | + if (sharedInstance == nil) { | |
20 | + [[self alloc] init]; // assignment not done here | |
21 | + } | |
22 | + } | |
23 | + return sharedInstance; | |
24 | +} | |
25 | + | |
26 | ++ (id)allocWithZone:(NSZone *)zone | |
27 | +{ | |
28 | + @synchronized(self) { | |
29 | + if (sharedInstance == nil) { | |
30 | + sharedInstance = [super allocWithZone:zone]; | |
31 | + return sharedInstance; // assignment and return on first allocation | |
32 | + } | |
33 | + } | |
34 | + return nil; //on subsequent allocation attempts return nil | |
35 | +} | |
36 | + | |
37 | +- (id)copyWithZone:(NSZone *)zone | |
38 | +{ | |
39 | + return self; | |
40 | +} | |
41 | + | |
42 | +- (id)retain | |
43 | +{ | |
44 | + return self; | |
45 | +} | |
46 | + | |
47 | +- (unsigned)retainCount | |
48 | +{ | |
49 | + return UINT_MAX; //denotes an object that cannot be released | |
50 | +} | |
51 | + | |
52 | +- (void)release | |
53 | +{ | |
54 | + //do nothing | |
55 | +} | |
56 | + | |
57 | +- (id)autorelease | |
58 | +{ | |
59 | + return self; | |
60 | +} | |
61 | + | |
62 | +#pragma mark- | |
63 | +- (id)init | |
64 | +{ | |
65 | + [super initWithWindowNibName:@"XspfQTImformation"]; | |
66 | + observedDocs = [[NSMutableArray array] retain]; | |
67 | + return self; | |
68 | +} | |
69 | + | |
70 | +- (void)windowDidLoad | |
71 | +{ | |
72 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
73 | + [nc addObserver:self | |
74 | + selector:@selector(notifee:) | |
75 | + name:NSWindowDidBecomeMainNotification | |
76 | + object:nil]; | |
77 | + [nc addObserver:self | |
78 | + selector:@selector(notifee:) | |
79 | + name:NSWindowDidResignMainNotification | |
80 | + object:nil]; | |
81 | + [nc addObserver:self | |
82 | + selector:@selector(notifee:) | |
83 | + name:NSApplicationDidBecomeActiveNotification | |
84 | + object:NSApp]; | |
85 | + [nc addObserver:self | |
86 | + selector:@selector(notifee:) | |
87 | + name:NSApplicationDidHideNotification | |
88 | + object:NSApp]; | |
89 | + [nc addObserver:self | |
90 | + selector:@selector(notifee:) | |
91 | + name:NSApplicationDidHideNotification | |
92 | + object:NSApp]; | |
93 | + [nc addObserver:self | |
94 | + selector:@selector(xspfDocumentWillCloseNotification:) | |
95 | + name:XspfDocumentWillCloseNotification | |
96 | + object:nil]; | |
97 | + | |
98 | +} | |
99 | +- (void)dealloc | |
100 | +{ | |
101 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
102 | + [nc removeObserver:self]; | |
103 | + | |
104 | + [observedDocs release]; | |
105 | + | |
106 | + [super dealloc]; | |
107 | +} | |
108 | + | |
109 | +- (id)currentTrack | |
110 | +{ | |
111 | + id doc = [[NSDocumentController sharedDocumentController] currentDocument]; | |
112 | + if(!doc) return nil; | |
113 | + | |
114 | + if(![observedDocs containsObject:doc]) { | |
115 | + [doc addObserver:self | |
116 | + forKeyPath:@"trackList.qtMovie" | |
117 | + options:0 | |
118 | + context:NULL]; | |
119 | + [observedDocs addObject:doc]; | |
120 | + } | |
121 | + | |
122 | + return [doc valueForKeyPath:@"trackList.currentTrack"]; | |
123 | +} | |
124 | +- (id)movieAttributes | |
125 | +{ | |
126 | + id doc = [[NSDocumentController sharedDocumentController] currentDocument]; | |
127 | + if(!doc) return nil; | |
128 | + | |
129 | + if(![observedDocs containsObject:doc]) { | |
130 | + [doc addObserver:self | |
131 | + forKeyPath:@"trackList.qtMovie" | |
132 | + options:0 | |
133 | + context:NULL]; | |
134 | + [observedDocs addObject:doc]; | |
135 | + } | |
136 | + | |
137 | + return [doc valueForKeyPath:@"trackList.qtMovie.movieAttributes"]; | |
138 | +} | |
139 | +- (void)observeValueForKeyPath:(NSString *)keyPath | |
140 | + ofObject:(id)object | |
141 | + change:(NSDictionary *)change | |
142 | + context:(void *)context | |
143 | +{ | |
144 | + if([keyPath isEqualToString:@"trackList.qtMovie"]) { | |
145 | + [self willChangeValueForKey:@"movieAttributes"]; | |
146 | + [self didChangeValueForKey:@"movieAttributes"]; | |
147 | + [self willChangeValueForKey:@"currentTrack"]; | |
148 | + [self didChangeValueForKey:@"currentTrack"]; | |
149 | + } | |
150 | +} | |
151 | + | |
152 | +- (void)xspfDocumentWillCloseNotification:(id)notification | |
153 | +{ | |
154 | + id doc = [notification object]; | |
155 | + | |
156 | + [doc removeObserver:self forKeyPath:@"trackList.qtMovie"]; | |
157 | + [observedDocs removeObject:doc]; | |
158 | + [docController setContent:nil]; | |
159 | +} | |
160 | +- (void)notifee:(id)notification | |
161 | +{ | |
162 | + [self willChangeValueForKey:@"movieAttributes"]; | |
163 | + [self didChangeValueForKey:@"movieAttributes"]; | |
164 | + [self willChangeValueForKey:@"currentTrack"]; | |
165 | + [self didChangeValueForKey:@"currentTrack"]; | |
166 | +} | |
167 | + | |
168 | + | |
169 | +@end |
@@ -0,0 +1,19 @@ | ||
1 | +// | |
2 | +// XspfAppDelegate.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | + | |
12 | +@interface XspfAppDelegate : NSObject | |
13 | +{ | |
14 | + NSWindow *mainWindowStore; | |
15 | +} | |
16 | + | |
17 | +- (IBAction)openInformationPanel:(id)sender; | |
18 | +- (IBAction)playedTrack:(id)sender; | |
19 | +@end |
@@ -0,0 +1,133 @@ | ||
1 | +// | |
2 | +// XspfAppDelegate.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfAppDelegate.h" | |
10 | +#import "XspfValueTransformers.h" | |
11 | +#import "XspfQTInformationWindowController.h" | |
12 | + | |
13 | +@implementation XspfAppDelegate | |
14 | + | |
15 | ++ (void)initialize | |
16 | +{ | |
17 | + [NSValueTransformer setValueTransformer:[[[XspfQTTimeTransformer alloc] init] autorelease] | |
18 | + forName:@"XspfQTTimeTransformer"]; | |
19 | + [NSValueTransformer setValueTransformer:[[[XspfQTTimeDateTransformer alloc] init] autorelease] | |
20 | + forName:@"XspfQTTimeDateTransformer"]; | |
21 | +} | |
22 | + | |
23 | +- (void)awakeFromNib | |
24 | +{ | |
25 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
26 | + [nc addObserver:self | |
27 | + selector:@selector(windowDidBecomeMain:) | |
28 | + name:NSWindowDidBecomeMainNotification | |
29 | + object:nil]; | |
30 | + [nc addObserver:self | |
31 | + selector:@selector(windowWillClose:) | |
32 | + name:NSWindowWillCloseNotification | |
33 | + object:nil]; | |
34 | +} | |
35 | +- (void)dealloc | |
36 | +{ | |
37 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
38 | + [nc removeObserver:self]; | |
39 | + | |
40 | + [super dealloc]; | |
41 | +} | |
42 | + | |
43 | +#pragma mark ### Actions ### | |
44 | +- (IBAction)playedTrack:(id)sender | |
45 | +{ | |
46 | + // do noting. | |
47 | +} | |
48 | +- (IBAction)openInformationPanel:(id)sender | |
49 | +{ | |
50 | + XspfQTInformationWindowController *wc; | |
51 | + wc = [XspfQTInformationWindowController sharedInstance]; | |
52 | + [wc showWindow:sender]; | |
53 | +} | |
54 | +- (IBAction)togglePlayAndPause:(id)sender | |
55 | +{ | |
56 | + [[mainWindowStore windowController] togglePlayAndPause:sender]; | |
57 | +} | |
58 | +- (IBAction)nextTrack:(id)sender | |
59 | +{ | |
60 | + [[mainWindowStore windowController] nextTrack:sender]; | |
61 | +} | |
62 | +- (IBAction)previousTrack:(id)sender | |
63 | +{ | |
64 | + [[mainWindowStore windowController] previousTrack:sender]; | |
65 | +} | |
66 | + | |
67 | +#pragma mark ### NSMenu valivation ### | |
68 | +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem | |
69 | +{ | |
70 | + if([menuItem action] == @selector(openInformationPanel:)) { | |
71 | + return YES; | |
72 | + } | |
73 | + | |
74 | + if([menuItem tag] == 10000) { | |
75 | + NSWindow *m = mainWindowStore; | |
76 | + if(!m) { | |
77 | + m = [NSApp mainWindow]; | |
78 | + } | |
79 | + NSString *title = [m valueForKeyPath:@"windowController.document.trackList.currentTrack.title"]; | |
80 | + if(title) { | |
81 | + [menuItem setTitle:[NSString stringWithFormat:@"%@ played", title]]; | |
82 | + } else { | |
83 | + [menuItem setTitle:@"Played Track Title"]; | |
84 | + } | |
85 | + return NO; | |
86 | + } | |
87 | + if(!mainWindowStore) { | |
88 | + return NO; | |
89 | + } | |
90 | + | |
91 | + return YES; | |
92 | +} | |
93 | + | |
94 | +- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender | |
95 | +{ | |
96 | + return NO; | |
97 | +} | |
98 | + | |
99 | +- (void)storeMainWindow | |
100 | +{ | |
101 | + mainWindowStore = [NSApp mainWindow]; | |
102 | +} | |
103 | +- (void)unsaveMainWindow | |
104 | +{ | |
105 | + mainWindowStore = nil; | |
106 | +} | |
107 | +- (void)applicationWillHide:(NSNotification *)notification | |
108 | +{ | |
109 | + [self storeMainWindow]; | |
110 | +} | |
111 | +- (void)applicationWillResignActive:(NSNotification *)notification | |
112 | +{ | |
113 | + [self storeMainWindow]; | |
114 | +} | |
115 | +- (void)applicationDidUnhide:(NSNotification *)notification | |
116 | +{ | |
117 | + [self unsaveMainWindow]; | |
118 | +} | |
119 | +- (void)applicationDidBecomeActive:(NSNotification *)notification | |
120 | +{ | |
121 | + [self unsaveMainWindow]; | |
122 | +} | |
123 | + | |
124 | +- (void)windowDidBecomeMain:(NSNotification *)notification | |
125 | +{ | |
126 | + [self storeMainWindow]; | |
127 | +} | |
128 | +- (void)windowWillClose:(NSNotification *)notification | |
129 | +{ | |
130 | + [self unsaveMainWindow]; | |
131 | +} | |
132 | + | |
133 | +@end |
@@ -0,0 +1,21 @@ | ||
1 | +// | |
2 | +// XspfQTInformationWindowController.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/09/14. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | + | |
12 | +@interface XspfQTInformationWindowController : NSWindowController | |
13 | +{ | |
14 | + IBOutlet NSObjectController *docController; | |
15 | + IBOutlet NSObjectController *currentTrackController; | |
16 | + NSMutableArray *observedDocs; | |
17 | +} | |
18 | + | |
19 | ++ (XspfQTInformationWindowController *)sharedInstance; | |
20 | + | |
21 | +@end |
@@ -0,0 +1,217 @@ | ||
1 | +// | |
2 | +// MyDocument.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright masakih 2008 . All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfDocument.h" | |
10 | +#import "XspfComponent.h" | |
11 | +#import "XspfMovieWindowController.h" | |
12 | +#import "XspfPlayListWindowController.h" | |
13 | + | |
14 | +@interface XspfDocument (Private) | |
15 | +- (void)setTrackList:(XspfComponent *)newList; | |
16 | +- (XspfComponent *)trackList; | |
17 | +- (NSXMLDocument *)XMLDocument; | |
18 | +- (NSData *)outputData; | |
19 | +@end | |
20 | + | |
21 | +@implementation XspfDocument | |
22 | + | |
23 | +NSString *XspfDocumentWillCloseNotification = @"XspfDocumentWillCloseNotification"; | |
24 | + | |
25 | + | |
26 | +- (id)init | |
27 | +{ | |
28 | + self = [super init]; | |
29 | + if (self) { | |
30 | + | |
31 | + // Add your subclass-specific initialization here. | |
32 | + // If an error occurs here, send a [self release] message and return nil. | |
33 | + | |
34 | + } | |
35 | + return self; | |
36 | +} | |
37 | + | |
38 | +- (void)makeWindowControllers | |
39 | +{ | |
40 | + playListWindowController = [[XspfPlayListWindowController alloc] init]; | |
41 | + [self addWindowController:playListWindowController]; | |
42 | + | |
43 | + movieWindowController = [[XspfMovieWindowController alloc] init]; | |
44 | + [movieWindowController setShouldCloseDocument:YES]; | |
45 | + [self addWindowController:movieWindowController]; | |
46 | + [movieWindowController setQtMovie:[[self trackList] qtMovie]]; | |
47 | +} | |
48 | +//- (NSString *)windowNibName | |
49 | +//{ | |
50 | +// // Override returning the nib file name of the document | |
51 | +// // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead. | |
52 | +// return @"MyDocument"; | |
53 | +//} | |
54 | + | |
55 | +- (void)windowControllerDidLoadNib:(NSWindowController *)windowController | |
56 | +{ | |
57 | + [super windowControllerDidLoadNib:windowController]; | |
58 | + // Add any code here that needs to be executed once the windowController has loaded the document's window. | |
59 | +// [self setQtMovie:[[self trackList] qtMovie]]; | |
60 | +} | |
61 | + | |
62 | +- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError | |
63 | +{ | |
64 | + // Insert code here to write your document to data of the specified type. If the given outError != NULL, ensure that you set *outError when returning nil. | |
65 | + | |
66 | + // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead. | |
67 | + | |
68 | + // For applications targeted for Panther or earlier systems, you should use the deprecated API -dataRepresentationOfType:. In this case you can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead. | |
69 | + | |
70 | + return [self outputData]; | |
71 | + // | |
72 | + // | |
73 | + // | |
74 | + | |
75 | + if ( outError != NULL ) { | |
76 | + *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL]; | |
77 | + } | |
78 | + return nil; | |
79 | +} | |
80 | + | |
81 | +- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError | |
82 | +{ | |
83 | + // Insert code here to read your document from the given data of the specified type. If the given outError != NULL, ensure that you set *outError when returning NO. | |
84 | + | |
85 | + // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead. | |
86 | + | |
87 | + // For applications targeted for Panther or earlier systems, you should use the deprecated API -loadDataRepresentation:ofType. In this case you can also choose to override -readFromFile:ofType: or -loadFileWrapperRepresentation:ofType: instead. | |
88 | + | |
89 | + NSError *error = nil; | |
90 | + NSXMLDocument *d = [[[NSXMLDocument alloc] initWithData:data | |
91 | + options:0 | |
92 | + error:&error] autorelease]; | |
93 | + NSXMLElement *root = [d rootElement]; | |
94 | + | |
95 | + NSArray *trackListElems; | |
96 | + trackListElems = [root elementsForName:@"trackList"]; | |
97 | + if(!trackListElems || [trackListElems count] < 1) { | |
98 | + if ( outError != NULL ) { | |
99 | + *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL]; | |
100 | + } | |
101 | + return NO; | |
102 | + } | |
103 | + | |
104 | + id t = [XspfComponent xspfComponemtWithXMLElement:[trackListElems objectAtIndex:0]]; | |
105 | + if(![t title]) { | |
106 | + [t setTitle:[[[self fileURL] path] lastPathComponent]]; | |
107 | + } | |
108 | + [self setTrackList:t]; | |
109 | +// NSLog(@"trackList -> %@", trackList); | |
110 | + | |
111 | +// [self setQtMovie:[[self trackList] qtMovie]]; | |
112 | + | |
113 | + return YES; | |
114 | +} | |
115 | + | |
116 | +- (void)dealloc | |
117 | +{ | |
118 | + [trackList release]; | |
119 | + [playListWindowController release]; | |
120 | + [movieWindowController release]; | |
121 | + | |
122 | + [super dealloc]; | |
123 | +} | |
124 | +//- (NSString *)displayName | |
125 | +//{ | |
126 | +// NSString *trackTitle = [[[self trackList] currentTrack] title]; | |
127 | +// if(trackTitle) { | |
128 | +// return [NSString stringWithFormat:@"%@ - %@", | |
129 | +// [super displayName], trackTitle]; | |
130 | +// } | |
131 | +// | |
132 | +// return [super displayName]; | |
133 | +//} | |
134 | +- (void)close | |
135 | +{ | |
136 | + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
137 | + [nc postNotificationName:XspfDocumentWillCloseNotification object:self]; | |
138 | + | |
139 | + [self removeWindowController:playListWindowController]; | |
140 | + [playListWindowController release]; | |
141 | + playListWindowController = nil; | |
142 | + | |
143 | + [self removeWindowController:movieWindowController]; | |
144 | + [movieWindowController release]; | |
145 | + movieWindowController = nil; | |
146 | + | |
147 | + [super close]; | |
148 | +} | |
149 | + | |
150 | +- (IBAction)togglePlayAndPause:(id)sender | |
151 | +{ | |
152 | + [movieWindowController togglePlayAndPause:sender]; | |
153 | +} | |
154 | +- (IBAction)showPlayList:(id)sender | |
155 | +{ | |
156 | + [playListWindowController showWindow:self]; | |
157 | +} | |
158 | + | |
159 | +- (void)setTrackList:(XspfComponent *)newList | |
160 | +{ | |
161 | + if(trackList == newList) return; | |
162 | + | |
163 | + [trackList autorelease]; | |
164 | + trackList = [newList retain]; | |
165 | +} | |
166 | +- (XspfComponent *)trackList | |
167 | +{ | |
168 | + return trackList; | |
169 | +} | |
170 | + | |
171 | +- (void)setPlayTrackindex:(unsigned)index | |
172 | +{ | |
173 | + [[self trackList] setSelectionIndex:index]; | |
174 | +} | |
175 | + | |
176 | +- (NSData *)outputData | |
177 | +{ | |
178 | + return [[self XMLDocument] XMLDataWithOptions:NSXMLNodePrettyPrint]; | |
179 | +} | |
180 | +- (NSXMLDocument *)XMLDocument; | |
181 | +{ | |
182 | + id element = [[self trackList] XMLElement]; | |
183 | + | |
184 | + id root = [NSXMLElement elementWithName:@"playlist"]; | |
185 | + [root addChild:element]; | |
186 | + [root addAttribute:[NSXMLNode attributeWithName:@"version" | |
187 | + stringValue:@"0"]]; | |
188 | + [root addAttribute:[NSXMLNode attributeWithName:@"xmlns" | |
189 | + stringValue:@"http://xspf.org/ns/0/"]]; | |
190 | + | |
191 | + | |
192 | + id d = [[[NSXMLDocument alloc] initWithRootElement:root] autorelease]; | |
193 | + [d setVersion:@"1.0"]; | |
194 | + [d setCharacterEncoding:@"UTF-8"]; | |
195 | + | |
196 | + return d; | |
197 | +} | |
198 | + | |
199 | +- (void)insertItem:(XspfComponent *)item atIndex:(NSInteger)index | |
200 | +{ | |
201 | + // | |
202 | +} | |
203 | +- (void)removeItem:(XspfComponent *)item | |
204 | +{ | |
205 | + [movieWindowController stop]; | |
206 | + [[self trackList] removeChild:item]; | |
207 | +} | |
208 | + | |
209 | +- (IBAction)dump:(id)sender | |
210 | +{ | |
211 | + NSString *s = [[[NSString alloc] initWithData:[self outputData] | |
212 | + encoding:NSUTF8StringEncoding] autorelease]; | |
213 | + | |
214 | + NSLog(@"%@", s); | |
215 | +} | |
216 | +@end | |
217 | + |
@@ -0,0 +1,37 @@ | ||
1 | +// | |
2 | +// MyDocument.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright masakih 2008 . All rights reserved. | |
7 | +// | |
8 | + | |
9 | + | |
10 | +#import <Cocoa/Cocoa.h> | |
11 | +#import <QTKit/QTKit.h> | |
12 | + | |
13 | +@class XspfComponent; | |
14 | +@class XspfMovieWindowController; | |
15 | + | |
16 | +@interface XspfDocument : NSDocument | |
17 | +{ | |
18 | + XspfComponent* trackList; | |
19 | + XspfMovieWindowController *movieWindowController; | |
20 | + NSWindowController *playListWindowController; | |
21 | +} | |
22 | + | |
23 | +- (IBAction)togglePlayAndPause:(id)sender; | |
24 | +- (IBAction)showPlayList:(id)sender; | |
25 | +- (IBAction)dump:(id)sender; | |
26 | + | |
27 | +- (void)setTrackList:(XspfComponent *)newList; | |
28 | +- (XspfComponent *)trackList; | |
29 | + | |
30 | +- (void)setPlayTrackindex:(unsigned)index; | |
31 | + | |
32 | +- (void)insertItem:(XspfComponent *)item atIndex:(NSInteger)index; | |
33 | +- (void)removeItem:(XspfComponent *)item; | |
34 | + | |
35 | +@end | |
36 | + | |
37 | +extern NSString *XspfDocumentWillCloseNotification; |
@@ -0,0 +1,205 @@ | ||
1 | +// | |
2 | +// XspfPlayListWindowController.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfPlayListWindowController.h" | |
10 | +#import "XspfDocument.h" | |
11 | +#import "XspfComponent.h" | |
12 | + | |
13 | + | |
14 | +@interface XspfPlayListWindowController(Private) | |
15 | +- (void)setObserveObject:(id)new; | |
16 | +@end | |
17 | + | |
18 | +@implementation XspfPlayListWindowController | |
19 | + | |
20 | +static NSString *const XspfQTPlayListItemType = @"XspfQTPlayListItemType"; | |
21 | + | |
22 | +- (id)init | |
23 | +{ | |
24 | + return [super initWithWindowNibName:@"XspfPlayList"]; | |
25 | +} | |
26 | + | |
27 | +- (void)awakeFromNib | |
28 | +{ | |
29 | + [listView setDoubleAction:@selector(changeCurrentTrack:)]; | |
30 | + [[self window] setReleasedWhenClosed:NO]; | |
31 | + | |
32 | + [trackListTree addObserver:self | |
33 | + forKeyPath:@"selection" | |
34 | + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | |
35 | + context:NULL]; | |
36 | + [self setObserveObject:[trackListTree valueForKeyPath:@"selection.self"]]; | |
37 | + | |
38 | + [listView expandItem:[listView itemAtRow:0]]; | |
39 | + | |
40 | +// [listView registerForDraggedTypes:[NSArray arrayWithObject:XspfQTPlayListItemType]]; | |
41 | +} | |
42 | +- (void)dealloc | |
43 | +{ | |
44 | + [trackListTree removeObserver:self forKeyPath:@"selection"]; | |
45 | + [self setObserveObject:nil]; | |
46 | + | |
47 | + [super dealloc]; | |
48 | +} | |
49 | + | |
50 | +- (IBAction)changeCurrentTrack:(id)sender | |
51 | +{ | |
52 | + id selections = [trackListTree selectedObjects]; | |
53 | + if([selections count] == 0) return; | |
54 | + | |
55 | + NSIndexPath *selectionIndexPath = [trackListTree selectionIndexPath]; | |
56 | +// NSLog(@"Selection %@", selectionIndexPath); | |
57 | +// NSLog(@"Selection index %d", [selectionIndexPath indexAtPosition:1]); | |
58 | + | |
59 | + if([selectionIndexPath length] > 1) { | |
60 | + [[self document] setPlayTrackindex:[selectionIndexPath indexAtPosition:1]]; | |
61 | + } | |
62 | +} | |
63 | + | |
64 | +- (void)keyDown:(NSEvent *)theEvent | |
65 | +{ | |
66 | + if([theEvent isARepeat]) return; | |
67 | + | |
68 | + unsigned short code = [theEvent keyCode]; | |
69 | + if(code == 49 /* space bar */) { | |
70 | + [[self document] togglePlayAndPause:self]; | |
71 | + } | |
72 | +} | |
73 | +- (void)deleteBackward:(id)sender | |
74 | +{ | |
75 | + id selection = [[trackListTree selection] representedObject]; | |
76 | + [[self document] removeItem:selection]; | |
77 | +} | |
78 | +- (void)deleteForward:(id)sender | |
79 | +{ | |
80 | + id selection = [[trackListTree selection] representedObject]; | |
81 | + [[self document] removeItem:selection]; | |
82 | +} | |
83 | + | |
84 | +- (BOOL)windowShouldClose:(id)sender | |
85 | +{ | |
86 | + [sender orderOut:self]; | |
87 | + | |
88 | + return NO; | |
89 | +} | |
90 | +- (void)setObserveObject:(id)new | |
91 | +{ | |
92 | + if(obseveObject == new) return; | |
93 | + | |
94 | + [obseveObject removeObserver:self forKeyPath:@"title"]; | |
95 | + | |
96 | + obseveObject = new; | |
97 | + [obseveObject addObserver:self | |
98 | + forKeyPath:@"title" | |
99 | + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | |
100 | + context:NULL]; | |
101 | +} | |
102 | +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context | |
103 | +{ | |
104 | + if([keyPath isEqualToString:@"selection"]) { | |
105 | + id new = [object valueForKeyPath:@"selection.self"]; | |
106 | + [self setObserveObject:new]; | |
107 | + } | |
108 | + | |
109 | + if([keyPath isEqualToString:@"title"]) { | |
110 | + id new = [change objectForKey:NSKeyValueChangeNewKey]; | |
111 | + id old = [change objectForKey:NSKeyValueChangeOldKey]; | |
112 | + | |
113 | + if(new == old) return; | |
114 | + if([new isEqualTo:old]) return; | |
115 | + | |
116 | + id um = [[self document] undoManager]; | |
117 | + [um registerUndoWithTarget:obseveObject | |
118 | + selector:@selector(setTitle:) | |
119 | + object:old]; | |
120 | + } | |
121 | +} | |
122 | + | |
123 | + | |
124 | +- (BOOL)outlineView:(NSOutlineView *)outlineView | |
125 | + writeItems:(NSArray *)items | |
126 | + toPasteboard:(NSPasteboard *)pasteboard | |
127 | +{ | |
128 | + if([items count] > 1) return NO; | |
129 | + | |
130 | + id item = [[items objectAtIndex:0] representedObject]; | |
131 | + | |
132 | + if(![item isKindOfClass:[XspfComponent class]]) { | |
133 | + NSLog(@"Ouch! %@", NSStringFromClass([item class])); | |
134 | + return NO; | |
135 | + } | |
136 | + if(![item isLeaf]) return NO; | |
137 | + | |
138 | + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:item]; | |
139 | + if(!data) { | |
140 | + NSLog(@"Could not archive."); | |
141 | + return NO; | |
142 | + } | |
143 | + | |
144 | + [pasteboard declareTypes:[NSArray arrayWithObject:XspfQTPlayListItemType] | |
145 | + owner:self]; | |
146 | + [pasteboard setData:data | |
147 | + forType:XspfQTPlayListItemType]; | |
148 | + return YES; | |
149 | +} | |
150 | +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView | |
151 | + validateDrop:(id <NSDraggingInfo>)info | |
152 | + proposedItem:(id)item | |
153 | + proposedChildIndex:(NSInteger)index | |
154 | +{ | |
155 | + if([item isLeaf]) { | |
156 | + return NSDragOperationNone; | |
157 | + } | |
158 | + | |
159 | + id pb = [info draggingPasteboard]; | |
160 | + | |
161 | + if(![[pb types] containsObject:XspfQTPlayListItemType]) { | |
162 | + return NSDragOperationNone; | |
163 | + } | |
164 | + | |
165 | + return NSDragOperationMove; | |
166 | +} | |
167 | +- (BOOL)outlineView:(NSOutlineView *)outlineView | |
168 | + acceptDrop:(id <NSDraggingInfo>)info | |
169 | + item:(id)item | |
170 | + childIndex:(NSInteger)index | |
171 | +{ | |
172 | + if([item isLeaf]) { | |
173 | + return NO; | |
174 | + } | |
175 | + | |
176 | + id pb = [info draggingPasteboard]; | |
177 | + | |
178 | + NSData *data = [pb dataForType:XspfQTPlayListItemType]; | |
179 | + if(!data) return NO; | |
180 | + | |
181 | + id newItem = [NSKeyedUnarchiver unarchiveObjectWithData:data]; | |
182 | + if(!newItem) return NO; | |
183 | + | |
184 | +// NSLog(@"new item class is %@\n%@", NSStringFromClass([newItem class]), newItem); | |
185 | + [[self document] removeItem:newItem]; | |
186 | + | |
187 | + return YES; | |
188 | +} | |
189 | + | |
190 | +@end | |
191 | + | |
192 | +@implementation XspfThowSpacebarKeyDownOutlineView | |
193 | +- (void)keyDown:(NSEvent *)theEvent | |
194 | +{ | |
195 | + unsigned short code = [theEvent keyCode]; | |
196 | + if(code == 49 /* space bar */) { | |
197 | + if(_delegate && [_delegate respondsToSelector:@selector(keyDown:)]) { | |
198 | + [_delegate keyDown:theEvent]; | |
199 | + } | |
200 | + } | |
201 | + | |
202 | + [super keyDown:theEvent]; | |
203 | +} | |
204 | + | |
205 | +@end |
@@ -0,0 +1,43 @@ | ||
1 | +// | |
2 | +// XspfMovieWindowController.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | +#import <QTKit/QTKit.h> | |
11 | + | |
12 | + | |
13 | +@interface XspfMovieWindowController : NSWindowController | |
14 | +{ | |
15 | + IBOutlet QTMovieView *qtView; | |
16 | + IBOutlet NSButton *playButton; | |
17 | + | |
18 | + NSWindow *fullscreenWindow; | |
19 | + NSRect nomalModeSavedFrame; | |
20 | + BOOL fullScreenMode; | |
21 | + | |
22 | + QTMovie *qtMovie; | |
23 | + NSTimer *updateTime; | |
24 | + | |
25 | + NSPoint prevMouse; | |
26 | + NSDate *prevMouseMovedDate; | |
27 | + | |
28 | + NSSize windowSizeWithoutQTView; | |
29 | +} | |
30 | + | |
31 | +- (IBAction)togglePlayAndPause:(id)sender; | |
32 | +- (IBAction)toggleFullScreenMode:(id)sender; | |
33 | +- (IBAction)forwardTagValueSecends:(id)sender; | |
34 | +- (IBAction)backwardTagValueSecends:(id)sender; | |
35 | +- (IBAction)nextTrack:(id)sender; | |
36 | +- (IBAction)previousTrack:(id)sender; | |
37 | + | |
38 | +- (void)play; | |
39 | +- (void)stop; | |
40 | + | |
41 | +- (void)setQtMovie:(QTMovie *)qt; | |
42 | +- (QTMovie *)qtMovie; | |
43 | +@end |
@@ -0,0 +1,58 @@ | ||
1 | +// | |
2 | +// XspfComponent.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | +#import <QTKit/QTKit.h> | |
11 | + | |
12 | +@interface XspfComponent : NSObject <NSCoding> | |
13 | +{ | |
14 | + NSString *title; | |
15 | + BOOL isSelected; | |
16 | + NSIndexPath *selectionIndexPath; | |
17 | + | |
18 | + XspfComponent *parent; // not retained. | |
19 | +} | |
20 | + | |
21 | ++ (id)xspfComponemtWithXMLElement:(NSXMLElement *)element; | |
22 | +- (id)initWithXMLElement:(NSXMLElement *)element; // abstract. | |
23 | + | |
24 | +- (NSXMLElement *)XMLElement; // abstract. | |
25 | + | |
26 | +- (QTMovie *)qtMovie; | |
27 | + | |
28 | +- (void)setTitle:(NSString *)title; | |
29 | +- (NSString *)title; | |
30 | + | |
31 | +// selection for playing. | |
32 | +- (BOOL)isSelected; | |
33 | +- (void)select; | |
34 | +- (void)deselect; | |
35 | +- (void)setSelectionIndex:(unsigned)index; | |
36 | +- (BOOL)setSelectionIndexPath:(NSIndexPath *)indexPath; | |
37 | +- (NSIndexPath *)selectionIndexPath; | |
38 | + | |
39 | +- (XspfComponent *)currentTrack; // default self; | |
40 | +- (void)next; // abstract. | |
41 | +- (void)previous; // abstract. | |
42 | + | |
43 | +- (void)setIsPlayed:(BOOL)state; | |
44 | +- (BOOL)isPlayed; | |
45 | + | |
46 | +- (XspfComponent *)parent; | |
47 | +- (NSArray *)children; // default nil. | |
48 | +- (unsigned)childrenCount; // default [[self children] count]. | |
49 | +- (BOOL)isLeaf; // default YES. | |
50 | + | |
51 | +- (void)addChild:(XspfComponent *)child; // not implemented. | |
52 | +- (void)removeChild:(XspfComponent *)child; // not implemented. | |
53 | +- (void)insertChild:(XspfComponent *)child atIndex:(unsigned)index; // not implemented. | |
54 | +- (void)removeChildAtIndex:(unsigned)index; //not implemented. | |
55 | +- (void)setParent:(XspfComponent *)parent; // Do not call directly. call in only -addChild: method. | |
56 | + | |
57 | + | |
58 | +@end |
@@ -0,0 +1,222 @@ | ||
1 | +// | |
2 | +// XspfComponent.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfComponent.h" | |
10 | +#import "XspfPlaceholderComponent.h" | |
11 | + | |
12 | +@implementation XspfComponent | |
13 | + | |
14 | ++ (id) allocWithZone:(NSZone *) zone | |
15 | +{ | |
16 | + if ([self class] == [XspfComponent class]) { | |
17 | + return [XspfPlaceholderComponent sharedInstance]; | |
18 | + } | |
19 | + | |
20 | + return [super allocWithZone:zone]; | |
21 | +} | |
22 | + | |
23 | ++ (id)xspfComponemtWithXMLElement:(NSXMLElement *)element | |
24 | +{ | |
25 | + return [[[self alloc] initWithXMLElement:element] autorelease]; | |
26 | +} | |
27 | +- (id)initWithXMLElement:(NSXMLElement *)element | |
28 | +{ | |
29 | + [super init]; | |
30 | + [self release]; | |
31 | + | |
32 | + [self doesNotRecognizeSelector:_cmd]; | |
33 | + | |
34 | + return nil; | |
35 | +} | |
36 | +- (void)dealloc | |
37 | +{ | |
38 | + [title release]; | |
39 | + [selectionIndexPath release]; | |
40 | + | |
41 | + [super dealloc]; | |
42 | +} | |
43 | + | |
44 | +- (NSXMLElement *)XMLElement | |
45 | +{ | |
46 | + [self doesNotRecognizeSelector:_cmd]; | |
47 | + | |
48 | + return nil; | |
49 | +} | |
50 | + | |
51 | +- (QTMovie *)qtMovie | |
52 | +{ | |
53 | + return nil; | |
54 | +} | |
55 | +- (NSDate *)duration | |
56 | +{ | |
57 | + return nil; | |
58 | +} | |
59 | +- (XspfComponent *)parent | |
60 | +{ | |
61 | + return parent; | |
62 | +} | |
63 | +- (NSArray *)children | |
64 | +{ | |
65 | + return nil; | |
66 | +} | |
67 | +- (unsigned)childrenCount | |
68 | +{ | |
69 | + return [[self children] count]; | |
70 | +} | |
71 | +- (BOOL)isLeaf | |
72 | +{ | |
73 | + return YES; | |
74 | +} | |
75 | + | |
76 | +- (void)setParent:(XspfComponent *)new | |
77 | +{ | |
78 | + parent = new; | |
79 | +} | |
80 | +- (void)addChild:(XspfComponent *)child | |
81 | +{ | |
82 | + [self doesNotRecognizeSelector:_cmd]; | |
83 | +} | |
84 | +- (void)removeChild:(XspfComponent *)child | |
85 | +{ | |
86 | + [self doesNotRecognizeSelector:_cmd]; | |
87 | +} | |
88 | +- (void)insertChild:(XspfComponent *)child atIndex:(unsigned)index | |
89 | +{ | |
90 | + [self doesNotRecognizeSelector:_cmd]; | |
91 | +} | |
92 | +- (void)removeChildAtIndex:(unsigned)index | |
93 | +{ | |
94 | + [self doesNotRecognizeSelector:_cmd]; | |
95 | +} | |
96 | +- (void)setTitle:(NSString *)new | |
97 | +{ | |
98 | + if(title == new) return; | |
99 | + if([title isEqualTo:new]) return; | |
100 | + | |
101 | + [title autorelease]; | |
102 | + title = [new copy]; | |
103 | +} | |
104 | +- (NSString *)title | |
105 | +{ | |
106 | + return title; | |
107 | +} | |
108 | +- (BOOL)isSelected | |
109 | +{ | |
110 | + return isSelected; | |
111 | +} | |
112 | +- (void)select | |
113 | +{ | |
114 | + [self willChangeValueForKey:@"isSelected"]; | |
115 | + isSelected = YES; | |
116 | + [self didChangeValueForKey:@"isSelected"]; | |
117 | +} | |
118 | +- (void)deselect | |
119 | +{ | |
120 | + [self willChangeValueForKey:@"isSelected"]; | |
121 | + isSelected = NO; | |
122 | + [self didChangeValueForKey:@"isSelected"]; | |
123 | +} | |
124 | +- (void)setSelectionIndex:(unsigned)index | |
125 | +{ | |
126 | + [self doesNotRecognizeSelector:_cmd]; | |
127 | + | |
128 | + // 現在値と違うなら現在値をdeselect | |
129 | + | |
130 | + // 新しい値をselect | |
131 | +} | |
132 | +- (BOOL)setSelectionIndexPath:(NSIndexPath *)indexPath | |
133 | +{ | |
134 | + unsigned length = [indexPath length]; | |
135 | + if(length == 0) { | |
136 | + return NO; | |
137 | + } | |
138 | + unsigned firstIndex = [indexPath indexAtPosition:0]; | |
139 | + if(firstIndex > [self childrenCount]) { | |
140 | + return NO; | |
141 | + } | |
142 | + | |
143 | + XspfComponent *firstIndexedChild = [[self children] objectAtIndex:firstIndex]; | |
144 | + if(length != 1) { | |
145 | + NSIndexPath *deletedFirstIndex = nil; | |
146 | + unsigned *indexP = NULL; | |
147 | + @try { | |
148 | + indexP = calloc(sizeof(unsigned), length); | |
149 | + if(!indexP) { | |
150 | + [NSException raise:NSMallocException | |
151 | + format:@"Not enough memory"]; | |
152 | + } | |
153 | + [indexPath getIndexes:indexP]; | |
154 | + deletedFirstIndex = [NSIndexPath indexPathWithIndexes:indexP + 1 | |
155 | + length:length - 1]; | |
156 | + } | |
157 | + @catch (id ex) { | |
158 | + @throw; | |
159 | + } | |
160 | + @finally{ | |
161 | + free(indexP); | |
162 | + } | |
163 | + if(!deletedFirstIndex || | |
164 | + ![firstIndexedChild setSelectionIndexPath:deletedFirstIndex]) { | |
165 | + return NO; | |
166 | + } | |
167 | + } else { | |
168 | + [self setSelectionIndex:firstIndex]; | |
169 | + } | |
170 | + if(!isSelected) { | |
171 | + [self select]; | |
172 | + } | |
173 | + [selectionIndexPath autorelease]; | |
174 | + selectionIndexPath = [indexPath retain]; | |
175 | + | |
176 | + return YES; | |
177 | +} | |
178 | +- (NSIndexPath *)selectionIndexPath | |
179 | +{ | |
180 | + return selectionIndexPath; | |
181 | +} | |
182 | +- (void)setIsPlayed:(BOOL)state {} // do nothing. | |
183 | +- (BOOL)isPlayed | |
184 | +{ | |
185 | + return NO; | |
186 | +} | |
187 | +- (XspfComponent *)currentTrack | |
188 | +{ | |
189 | + return self; | |
190 | +} | |
191 | +- (void)next | |
192 | +{ | |
193 | + [self doesNotRecognizeSelector:_cmd]; | |
194 | +} | |
195 | +- (void)previous | |
196 | +{ | |
197 | + [self doesNotRecognizeSelector:_cmd]; | |
198 | +} | |
199 | + | |
200 | +- (void)encodeWithCoder:(NSCoder *)aCoder | |
201 | +{ | |
202 | + NSString *string = [[self XMLElement] XMLString]; | |
203 | + [aCoder encodeObject:string forKey:@"XspfQTComponentXMLStringCodingKey"]; | |
204 | +} | |
205 | +- (id)initWithCoder:(NSCoder *)aDecoder | |
206 | +{ | |
207 | + [super init]; | |
208 | + [self autorelease]; | |
209 | + | |
210 | + id string = [aDecoder decodeObjectForKey:@"XspfQTComponentXMLStringCodingKey"]; | |
211 | + | |
212 | + NSError *error = nil; | |
213 | + NSXMLElement *element = [[[NSXMLElement alloc] initWithXMLString:string error:&error] autorelease]; | |
214 | + if(error) { | |
215 | + NSLog(@"%@", error); | |
216 | + return nil; | |
217 | + } | |
218 | + | |
219 | + return [[[self class] alloc] initWithXMLElement:element]; | |
220 | +} | |
221 | + | |
222 | +@end |
@@ -0,0 +1,78 @@ | ||
1 | +// | |
2 | +// XspfPlaceholderComponent.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/09/06. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfPlaceholderComponent.h" | |
10 | +#import "XspfTrackList.h" | |
11 | +#import "XspfTrack.h" | |
12 | + | |
13 | +@implementation XspfPlaceholderComponent | |
14 | +static XspfPlaceholderComponent *sharedInstance = nil; | |
15 | + | |
16 | ++ (XspfPlaceholderComponent *)sharedInstance | |
17 | +{ | |
18 | + @synchronized(self) { | |
19 | + if (sharedInstance == nil) { | |
20 | + [[self alloc] init]; // assignment not done here | |
21 | + } | |
22 | + } | |
23 | + return sharedInstance; | |
24 | +} | |
25 | + | |
26 | ++ (id)allocWithZone:(NSZone *)zone | |
27 | +{ | |
28 | + @synchronized(self) { | |
29 | + if (sharedInstance == nil) { | |
30 | + sharedInstance = [super allocWithZone:zone]; | |
31 | + return sharedInstance; // assignment and return on first allocation | |
32 | + } | |
33 | + } | |
34 | + return nil; //on subsequent allocation attempts return nil | |
35 | +} | |
36 | + | |
37 | +- (id)copyWithZone:(NSZone *)zone | |
38 | +{ | |
39 | + return self; | |
40 | +} | |
41 | + | |
42 | +- (id)retain | |
43 | +{ | |
44 | + return self; | |
45 | +} | |
46 | + | |
47 | +- (unsigned)retainCount | |
48 | +{ | |
49 | + return UINT_MAX; //denotes an object that cannot be released | |
50 | +} | |
51 | + | |
52 | +- (void)release | |
53 | +{ | |
54 | + //do nothing | |
55 | +} | |
56 | + | |
57 | +- (id)autorelease | |
58 | +{ | |
59 | + return self; | |
60 | +} | |
61 | + | |
62 | +#pragma mark ### initializers ### | |
63 | +- (id)initWithXMLElement:(NSXMLElement *)element | |
64 | +{ | |
65 | + NSString *name = [element name]; | |
66 | + if(!name) return nil; | |
67 | + if([name isEqualToString:@""]) return nil; | |
68 | + | |
69 | + if([name isEqualToString:@"trackList"]) { | |
70 | + return [[XspfTrackList alloc] initWithXMLElement:element]; | |
71 | + } | |
72 | + if([name isEqualToString:@"track"]) { | |
73 | + return [[XspfTrack alloc] initWithXMLElement:element]; | |
74 | + } | |
75 | + | |
76 | + return nil; | |
77 | +} | |
78 | +@end |
@@ -0,0 +1,51 @@ | ||
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
3 | +<plist version="1.0"> | |
4 | +<dict> | |
5 | + <key>CFBundleDevelopmentRegion</key> | |
6 | + <string>English</string> | |
7 | + <key>CFBundleDocumentTypes</key> | |
8 | + <array> | |
9 | + <dict> | |
10 | + <key>CFBundleTypeExtensions</key> | |
11 | + <array> | |
12 | + <string>xspf</string> | |
13 | + </array> | |
14 | + <key>CFBundleTypeIconFile</key> | |
15 | + <string></string> | |
16 | + <key>CFBundleTypeName</key> | |
17 | + <string>XML Shareable Playlist Format</string> | |
18 | + <key>CFBundleTypeOSTypes</key> | |
19 | + <array> | |
20 | + <string>????</string> | |
21 | + </array> | |
22 | + <key>CFBundleTypeRole</key> | |
23 | + <string>Editor</string> | |
24 | + <key>NSDocumentClass</key> | |
25 | + <string>XspfDocument</string> | |
26 | + </dict> | |
27 | + </array> | |
28 | + <key>CFBundleExecutable</key> | |
29 | + <string>${EXECUTABLE_NAME}</string> | |
30 | + <key>CFBundleIconFile</key> | |
31 | + <string></string> | |
32 | + <key>CFBundleIdentifier</key> | |
33 | + <string>com.masakih.XspfQT</string> | |
34 | + <key>CFBundleInfoDictionaryVersion</key> | |
35 | + <string>6.0</string> | |
36 | + <key>CFBundleName</key> | |
37 | + <string>${PRODUCT_NAME}</string> | |
38 | + <key>CFBundlePackageType</key> | |
39 | + <string>APPL</string> | |
40 | + <key>CFBundleShortVersionString</key> | |
41 | + <string>1.1</string> | |
42 | + <key>CFBundleSignature</key> | |
43 | + <string>????</string> | |
44 | + <key>CFBundleVersion</key> | |
45 | + <string>%%%%REVISION%%%%</string> | |
46 | + <key>NSMainNibFile</key> | |
47 | + <string>MainMenu</string> | |
48 | + <key>NSPrincipalClass</key> | |
49 | + <string>NSApplication</string> | |
50 | +</dict> | |
51 | +</plist> |
@@ -0,0 +1,23 @@ | ||
1 | +// | |
2 | +// XspfPlayListWindowController.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | +@interface XspfPlayListWindowController : NSWindowController | |
12 | +{ | |
13 | + IBOutlet NSOutlineView *listView; | |
14 | + IBOutlet NSTreeController *trackListTree; | |
15 | + | |
16 | + id obseveObject; | |
17 | +} | |
18 | + | |
19 | +@end | |
20 | + | |
21 | + | |
22 | +@interface XspfThowSpacebarKeyDownOutlineView : NSOutlineView | |
23 | +@end |
@@ -0,0 +1,23 @@ | ||
1 | +// | |
2 | +// XspfTrackList.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | +#import "XspfComponent.h" | |
11 | + | |
12 | + | |
13 | +@interface XspfTrackList : XspfComponent | |
14 | +{ | |
15 | + NSMutableArray *tracks; | |
16 | + | |
17 | + unsigned currentIndex; | |
18 | +} | |
19 | + | |
20 | +- (void)setCurrentIndex:(unsigned)index; | |
21 | +- (unsigned)currentIndex; | |
22 | + | |
23 | +@end |
@@ -0,0 +1,32 @@ | ||
1 | +// | |
2 | +// XspfTrack.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | +#import <QTKit/QTKit.h> | |
11 | +#import "XspfComponent.h" | |
12 | + | |
13 | + | |
14 | +@interface XspfTrack : XspfComponent | |
15 | +{ | |
16 | + NSURL *location; | |
17 | + | |
18 | + QTMovie *movie; | |
19 | + | |
20 | + NSDate *savedDate; | |
21 | + | |
22 | + BOOL isPlayed; | |
23 | +} | |
24 | + | |
25 | +- (void)setLocation:(NSURL *)location; | |
26 | +- (void)setLocationString:(NSString *)location; | |
27 | +- (NSURL *)location; | |
28 | +- (NSString *)locationString; | |
29 | + | |
30 | +- (void)purgeQTMovie; | |
31 | + | |
32 | +@end |
@@ -0,0 +1,17 @@ | ||
1 | +// | |
2 | +// XspfPlaceholderComponent.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/09/06. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | +#import "XspfComponent.h" | |
11 | + | |
12 | +@interface XspfPlaceholderComponent : XspfComponent | |
13 | +{ | |
14 | + | |
15 | +} | |
16 | ++ (XspfPlaceholderComponent *)sharedInstance; | |
17 | +@end |
@@ -0,0 +1,14 @@ | ||
1 | +// | |
2 | +// main.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/29. | |
6 | +// Copyright masakih 2008 . All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | +int main(int argc, char *argv[]) | |
12 | +{ | |
13 | + return NSApplicationMain(argc, (const char **) argv); | |
14 | +} |
@@ -0,0 +1,17 @@ | ||
1 | +// | |
2 | +// XspfFullScreenWindow.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | + | |
12 | +@interface XspfFullScreenWindow : NSWindow | |
13 | +{ | |
14 | + | |
15 | +} | |
16 | + | |
17 | +@end |
@@ -0,0 +1,26 @@ | ||
1 | +// | |
2 | +// XspfFullScreenWindow.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import "XspfFullScreenWindow.h" | |
10 | + | |
11 | + | |
12 | +@implementation XspfFullScreenWindow | |
13 | +- (BOOL)canBecomeKeyWindow | |
14 | +{ | |
15 | + return YES; | |
16 | +} | |
17 | +- (void)cancelOperation:(id)sender | |
18 | +{ | |
19 | + id d = [self delegate]; | |
20 | + if(d && [d respondsToSelector:_cmd]) { | |
21 | + [d performSelector:_cmd withObject:sender]; | |
22 | + } | |
23 | + | |
24 | + [super cancelOperation:sender]; | |
25 | +} | |
26 | +@end |
@@ -0,0 +1,15 @@ | ||
1 | +// | |
2 | +// XspfValueTransformers.h | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <Cocoa/Cocoa.h> | |
10 | + | |
11 | + | |
12 | +@interface XspfQTTimeTransformer : NSValueTransformer | |
13 | +@end | |
14 | +@interface XspfQTTimeDateTransformer : NSValueTransformer | |
15 | +@end |
@@ -0,0 +1,103 @@ | ||
1 | +// | |
2 | +// XspfValueTransformers.m | |
3 | +// XspfQT | |
4 | +// | |
5 | +// Created by Hori,Masaki on 08/08/31. | |
6 | +// Copyright 2008 masakih. All rights reserved. | |
7 | +// | |
8 | + | |
9 | +#import <QTKit/QTKit.h> | |
10 | + | |
11 | +#import "XspfValueTransformers.h" | |
12 | + | |
13 | + | |
14 | +@implementation XspfQTTimeTransformer | |
15 | ++ (Class)transformedValueClass | |
16 | +{ | |
17 | + return [NSNumber class]; | |
18 | +} | |
19 | ++ (BOOL)allowsReverseTransformation | |
20 | +{ | |
21 | + return YES; | |
22 | +} | |
23 | +- (id)transformedValue:(id)value | |
24 | +{ | |
25 | + if(!value) return nil; | |
26 | + | |
27 | +// static first = YES; | |
28 | +// if(first) { | |
29 | +// first = NO; | |
30 | +// NSLog(@"%@ in value class -> %@", NSStringFromSelector(_cmd), NSStringFromClass([value class])); | |
31 | +// NSLog(@"value -> %@", value); | |
32 | +// } | |
33 | + | |
34 | + QTTime t = [value QTTimeValue]; | |
35 | + NSTimeInterval res; | |
36 | + | |
37 | + if(!QTGetTimeInterval(t, &res)) {return nil;} | |
38 | + | |
39 | + return [NSNumber numberWithDouble:res]; | |
40 | +} | |
41 | +- (id)reverseTransformedValue:(id)value | |
42 | +{ | |
43 | + if(!value) return nil; | |
44 | + | |
45 | +// static first = YES; | |
46 | +// if(first) { | |
47 | +// first = NO; | |
48 | +// NSLog(@"%@ in value class -> %@", NSStringFromSelector(_cmd), NSStringFromClass([value class])); | |
49 | +// NSLog(@"value -> %@", value); | |
50 | +// } | |
51 | + | |
52 | + QTTime t = QTMakeTimeWithTimeInterval([value doubleValue]); | |
53 | + | |
54 | + return [NSValue valueWithQTTime:t]; | |
55 | +} | |
56 | +@end | |
57 | + | |
58 | +@implementation XspfQTTimeDateTransformer | |
59 | ++ (Class)transformedValueClass | |
60 | +{ | |
61 | + return [NSDate class]; | |
62 | +} | |
63 | ++ (BOOL)allowsReverseTransformation | |
64 | +{ | |
65 | + return NO; | |
66 | +} | |
67 | +- (id)transformedValue:(id)value | |
68 | +{ | |
69 | + if(!value) return nil; | |
70 | + | |
71 | +// static first = YES; | |
72 | +// if(first) { | |
73 | +// first = NO; | |
74 | +// NSLog(@"%@ in value class -> %@", NSStringFromSelector(_cmd), NSStringFromClass([value class])); | |
75 | +// NSLog(@"value -> %@", value); | |
76 | +// } | |
77 | + | |
78 | + QTTime t = [value QTTimeValue]; | |
79 | + NSTimeInterval res; | |
80 | + | |
81 | + if(!QTGetTimeInterval(t, &res)) {return nil;} | |
82 | + | |
83 | + res -= [[NSTimeZone systemTimeZone] secondsFromGMT]; | |
84 | + | |
85 | + return [NSDate dateWithTimeIntervalSince1970:res]; | |
86 | +} | |
87 | +//- (id)reverseTransformedValue:(id)value | |
88 | +//{ | |
89 | +// if(!value) return nil; | |
90 | +// | |
91 | +// static first = YES; | |
92 | +// if(first) { | |
93 | +// first = NO; | |
94 | +// NSLog(@"%@ in value class -> %@", NSStringFromSelector(_cmd), NSStringFromClass([value class])); | |
95 | +// NSLog(@"value -> %@", value); | |
96 | +// } | |
97 | +// NSTimeInterval val = [value timeIntervalSinceReferenceDate]; | |
98 | +// val += [[NSTimeZone systemTimeZone] secondsFromGMT]; | |
99 | +// QTTime t = QTMakeTimeWithTimeInterval(val); | |
100 | +// | |
101 | +// return [NSValue valueWithQTTime:t]; | |
102 | +//} | |
103 | +@end | |
\ No newline at end of file |