Lots of code cleanup, bug fixes.
[MenuTunes.git] / iTunesRemote.m
1 #import "iTunesRemote.h"
2
3 @implementation iTunesRemote
4
5 + (id)remote
6 {
7     return [[[iTunesRemote alloc] init] autorelease];
8 }
9
10 - (NSString *)title
11 {
12     return @"iTunes Plug-in";
13 }
14
15 - (NSString *)information;
16 {
17     return @"Default MenuTunes plugin to control iTunes. Written by iThink Software.";
18 }
19
20 - (NSImage *)icon
21 {
22     return nil;
23 }
24
25 - (BOOL)begin
26 {
27     iTunesPSN = [self iTunesPSN];
28
29     //Register for application termination in NSWorkspace
30     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
31     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationTerminated:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
32
33     return YES;
34 }
35
36 - (BOOL)halt
37 {
38     iTunesPSN.highLongOfPSN = kNoProcess;
39
40     //Unregister for application termination in NSWorkspace
41     [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
42
43     return YES;
44 }
45
46 - (BOOL)isAppRunning
47 {
48     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
49     int i,count = [apps count];
50
51     for (i = 0; i < count; i++) {
52         if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"]
53                 isEqualToString:@"iTunes"]) {
54             return YES;
55         }
56     }
57     return NO;
58 }
59
60 - (PlayerState)playerState
61 {
62     long result = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlS'), from:'null'() }" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
63     
64     switch (result)
65     {
66         default:
67         case 'kPSS':
68             return stopped;
69         case 'kPSP':
70             return playing;
71         case 'kPSp':
72             return paused;
73         case 'kPSR':
74             return rewinding;
75         case 'kPSF':
76             return forwarding;
77     }
78     
79     return stopped;
80 }
81
82 - (NSArray *)playlists
83 {
84     long i = 0;
85     const signed long numPlaylists = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cPly'), '----':(), &subj:()" eventClass:@"core" eventID:@"cnte" appPSN:iTunesPSN];
86     NSMutableArray *playlists = [[NSMutableArray alloc] initWithCapacity:numPlaylists];
87     
88     for (i = 1; i <= numPlaylists; i++) {
89         const long j = i;
90         NSString *sendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cPly'), seld:long(%lu), from:'null'() } }",(unsigned long)j];
91         NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:sendStr eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN fixForAECrappiness:i];
92         [playlists addObject:theObj];
93     }
94     return [playlists autorelease];
95 }
96
97 - (int)numberOfSongsInPlaylistAtIndex:(int)index
98 {
99     return [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:[NSString stringWithFormat:@"kocl:type('cTrk'), '----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:'null'() }",index] eventClass:@"core" eventID:@"cnte" appPSN:iTunesPSN]
100
101 ;
102 }
103
104 - (NSString *)classOfPlaylistAtIndex:(int)index
105 {
106     int realResult = [[ITAppleEventCenter sharedCenter]
107                 sendTwoTierAEWithRequestedKeyForNumber:@"pcls" fromObjectByKey:@"pPla" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
108
109     if (realResult == 'cRTP') return @"radio tuner playlist";
110     else return @"playlist";
111 }
112
113 - (int)currentPlaylistIndex
114 {
115     int result;
116     result = [[ITAppleEventCenter sharedCenter]
117                 sendTwoTierAEWithRequestedKeyForNumber:@"pidx" fromObjectByKey:@"pPla" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
118     return result;
119 }
120
121 - (NSString *)songTitleAtIndex:(int)index
122 {
123     return [[ITAppleEventCenter sharedCenter] sendAEWithSendString:[NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }",index] eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
124 }
125
126 - (int)currentSongIndex
127 {
128     int result;
129     result = [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKeyForNumber:@"pidx" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
130     return result;
131 }
132
133 - (NSString *)currentSongTitle
134 {
135     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pnam" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
136 }
137
138 - (NSString *)currentSongArtist
139 {
140     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pArt" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
141 }
142
143 - (NSString *)currentSongAlbum
144 {
145     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pAlb" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
146 }
147
148 - (NSString *)currentSongGenre
149 {
150     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pGen" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
151 }
152
153 - (NSString *)currentSongLength
154 {
155     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pTim" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
156 }
157
158 - (NSString *)currentSongRemaining
159 {
160     long duration = [[ITAppleEventCenter sharedCenter]
161                         sendTwoTierAEWithRequestedKeyForNumber:@"pDur" fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
162     long current = [[ITAppleEventCenter sharedCenter]
163                         sendAEWithRequestedKeyForNumber:@"pPos" eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN];
164
165     return [[NSNumber numberWithLong:duration - current] stringValue];
166 }
167
168 - (NSArray *)eqPresets;
169 {
170     int i;
171     long numPresets = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cEQP'), '----':(), &subj:()" eventClass:@"core" eventID:@"cnte" appPSN:iTunesPSN];
172     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
173     
174     for (i = 1; i <= numPresets; i++) {
175         NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:[NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cEQP'), seld:long(%lu), from:'null'() } }",i] eventClass:@"core" eventID:@"getd" appPSN:iTunesPSN fixForAECrappiness:i];
176         if (theObj) {
177             [presets addObject:theObj];
178         }
179     }
180     return [presets autorelease];
181 }
182
183 - (int)currentEQPresetIndex
184 {
185     int result;
186     result = [[ITAppleEventCenter sharedCenter]
187                 sendTwoTierAEWithRequestedKeyForNumber:@"pidx"fromObjectByKey:@"pEQP" eventClass:@"core" eventID:@"getd"appPSN:iTunesPSN];
188     return result;
189 }
190
191 - (BOOL)play
192 {
193     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Play" appPSN:iTunesPSN];
194     return YES;
195 }
196
197 - (BOOL)pause
198 {
199     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Paus" appPSN:iTunesPSN];
200     return YES;
201 }
202
203 - (BOOL)goToNextSong
204 {
205     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Next" appPSN:iTunesPSN];
206     return YES;
207 }
208
209 - (BOOL)goToPreviousSong
210 {
211     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Prev" appPSN:iTunesPSN];
212     return YES;
213 }
214
215 - (BOOL)fastForward
216 {
217     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Fast" appPSN:iTunesPSN];
218     return YES;
219 }
220
221 - (BOOL)rewind
222 {
223     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Rwnd" appPSN:iTunesPSN];
224     return YES;
225 }
226
227
228 - (BOOL)switchToPlaylistAtIndex:(int)index
229 {
230     [[ITAppleEventCenter sharedCenter] sendAEWithSendString:[NSString stringWithFormat:@"'----':obj { form:'name', want:type('cPly'), seld:long(%lu), from:'null'() }",index] eventClass:@"hook" eventID:@"Play" appPSN:iTunesPSN];
231     return NO;
232 }
233
234 - (BOOL)switchToSongAtIndex:(int)index
235 {
236     [[ITAppleEventCenter sharedCenter] sendAEWithSendString:[NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }",index] eventClass:@"hook" eventID:@"Play" appPSN:iTunesPSN];
237     return NO;
238 }
239
240 - (BOOL)switchToEQAtIndex:(int)index
241 {
242     [[ITAppleEventCenter sharedCenter] sendAEWithSendString:[NSString stringWithFormat:@"data:obj { form:'name', want:type('cEQP'), seld:long(%lu), from:'null'() }, '----':obj { form:'prop', want:type('prop'), seld:type('pEQP'), from:'null'() }",index] eventClass:@"core" eventID:@"setd" appPSN:iTunesPSN];
243     [[ITAppleEventCenter sharedCenter] sendAEWithSendString:@"data:1, '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }" eventClass:@"core" eventID:@"setd" appPSN:iTunesPSN];
244     return NO;
245 }
246
247 - (ProcessSerialNumber)iTunesPSN
248 {
249     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
250     ProcessSerialNumber number;
251     int i, count = [apps count];
252     
253     number.highLongOfPSN = kNoProcess;
254     
255     for (i = 0; i < count; i++)
256     {
257         NSDictionary *curApp = [apps objectAtIndex:i];
258         
259         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
260         {
261             number.highLongOfPSN = [[curApp objectForKey:
262                 @"NSApplicationProcessSerialNumberHigh"] intValue];
263             number.lowLongOfPSN = [[curApp objectForKey:
264                 @"NSApplicationProcessSerialNumberLow"] intValue];
265         }
266     }
267     return number;
268 }
269
270 - (void)applicationLaunched:(NSNotification *)note
271 {
272     NSDictionary *info = [note userInfo];
273
274     if ([[info objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
275         iTunesPSN.highLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberHigh"] longValue];
276         iTunesPSN.lowLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberLow"] longValue];
277
278         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTRemoteAppDidLaunchNotification" object:nil];
279     }
280 }
281
282 - (void)applicationTerminated:(NSNotification *)note
283 {
284     NSDictionary *info = [note userInfo];
285
286     if ([[info objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
287         iTunesPSN.highLongOfPSN = kNoProcess;
288         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTRemoteAppDidTerminateNotification" object:nil];
289     }
290 }
291
292 @end