Should disable the status item when iTunes is locked.
[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 *)remoteTitle
11 {
12     return @"iTunes Remote";
13 }
14
15 - (NSString *)remoteInformation
16 {
17     return @"Default MenuTunes plugin to control iTunes, by iThink Software.";
18 }
19
20 - (NSImage *)remoteIcon
21 {
22     return nil;
23 }
24
25 - (BOOL)begin
26 {
27     ITDebugLog(@"iTunesRemote begun");
28     savedPSN = [self iTunesPSN];
29     return YES;
30 }
31
32 - (BOOL)halt
33 {
34     ITDebugLog(@"iTunesRemote halted");
35     return YES;
36 }
37
38 - (NSString *)playerFullName
39 {
40     return @"iTunes";
41 }
42
43 - (NSString *)playerSimpleName
44 {
45     return @"iTunes";
46 }
47
48 - (NSDictionary *)capabilities
49 {
50     return [NSDictionary dictionaryWithObjectsAndKeys:
51                 [NSNumber numberWithBool: YES], @"Remote",
52                 [NSNumber numberWithBool: YES], @"Basic Track Control",
53                 [NSNumber numberWithBool: YES], @"Track Information",
54                 [NSNumber numberWithBool: YES], @"Track Navigation",
55                 [NSNumber numberWithBool: YES], @"Upcoming Songs",
56                 [NSNumber numberWithBool: YES], @"Playlists",
57                 [NSNumber numberWithBool: YES], @"Volume",
58                 [NSNumber numberWithBool: YES], @"Shuffle",
59                 [NSNumber numberWithBool: YES], @"Repeat Modes",
60                 [NSNumber numberWithBool: YES], @"Equalizer",
61                 [NSNumber numberWithBool: YES], @"Track Rating",
62                 nil];
63 }
64
65 - (BOOL)showPrimaryInterface
66 {
67     ITDebugLog(@"Showing player primary interface.");
68     
69     if ([self playerRunningState] == ITMTRemotePlayerRunning) {
70         ITDebugLog(@"Showing player interface.");
71         //If not minimized and visible
72         if ( ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'getd', &savedPSN) booleanValue] == 0) &&
73                          ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pvis'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'getd', &savedPSN) booleanValue] != 0) &&
74              [[[[NSWorkspace sharedWorkspace] activeApplication] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"] ) {
75             //set minimized of browser window 1 to true
76                         ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'setd', &savedPSN);
77         } else {
78             //set minimized of browser window 1 to false
79                         ITSendAEWithString(@"data:long(0), '----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'setd', &savedPSN);
80         }
81         //set visible of browser window 1 to true
82                 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pvis'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'setd', &savedPSN);
83         //active iTunes
84                 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pisf'), from:'null'() }", 'core', 'setd', &savedPSN);
85         ITDebugLog(@"Done showing player primary interface.");
86         return YES;
87     } else {
88         NSString *path;
89         ITDebugLog(@"Launching player.");
90         if ( (path = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"]) ) {
91         } else {
92             path = [self playerFullName];
93         }
94         if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
95             ITDebugLog(@"Error Launching Player");
96             return NO;
97         }
98         return YES;
99     }
100 }
101
102 - (ITMTRemotePlayerRunningState)playerRunningState
103 {
104     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
105     int i;
106     int count = [apps count];
107     
108     for (i = 0; i < count; i++) {
109         if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
110             ITDebugLog(@"Player running state: 1");
111             return ITMTRemotePlayerRunning;
112         }
113     }
114     ITDebugLog(@"Player running state: 0");
115     return ITMTRemotePlayerNotRunning;
116 }
117
118 - (ITMTRemotePlayerPlayingState)playerPlayingState
119 {
120     SInt32 result;
121     
122     ITDebugLog(@"Getting player playing state");
123     result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlS'), from:'null'() }", 'core', 'getd', &savedPSN) int32Value];
124     switch (result)
125     {
126         case 'kPSP':
127             ITDebugLog(@"Getting player playing state done. Player state: Playing");
128             return ITMTRemotePlayerPlaying;
129         case 'kPSp':
130             ITDebugLog(@"Getting player playing state done. Player state: Paused");
131             return ITMTRemotePlayerPaused;
132         case 'kPSR':
133             ITDebugLog(@"Getting player playing state done. Player state: Rewinding");
134             return ITMTRemotePlayerRewinding;
135         case 'kPSF':
136             ITDebugLog(@"Getting player playing state done. Player state: Forwarding");
137             return ITMTRemotePlayerForwarding;
138         case 'kPSS':
139         default:
140             ITDebugLog(@"Getting player playing state done. Player state: Stopped");
141             return ITMTRemotePlayerStopped;
142     }
143     ITDebugLog(@"Getting player playing state done. Player state: Stopped");
144     return ITMTRemotePlayerStopped;
145 }
146
147 /*- (NSArray *)playlists
148 {
149     long i = 0;
150     const signed long numPlaylists = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cPly'), '----':()" eventClass:@"core" eventID:@"cnte" appPSN:savedPSN];
151     NSMutableArray *playlists = [[NSMutableArray alloc] initWithCapacity:numPlaylists];
152     
153     for (i = 1; i <= numPlaylists; i++) {
154         const long j = i;
155         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];
156         NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:sendStr eventClass:@"core" eventID:@"getd" appPSN:savedPSN];
157         [playlists addObject:theObj];
158     }
159     return [playlists autorelease];
160 }*/
161
162 //Full source awareness
163 - (NSArray *)playlists
164 {
165     unsigned long i, k;
166     SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
167     NSMutableArray *allSources = [[NSMutableArray alloc] init];
168     
169     ITDebugLog(@"Getting playlists.");
170     if (numSources == 0) {
171         ITDebugLog(@"No sources.");
172         return nil;
173     }
174     
175     for (k = 1; k <= numSources ; k++) {
176         SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() }",k], 'core', 'cnte', &savedPSN) int32Value];
177         SInt32 fourcc = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } }",k], 'core', 'getd', &savedPSN) int32Value];
178         NSString *sourceName = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } }",k], 'core', 'getd', &savedPSN) stringValue];
179         SInt32 index = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } }",k], 'core', 'getd', &savedPSN) int32Value];
180         unsigned long class;
181         if (sourceName) {
182             NSMutableArray *aSource = [[NSMutableArray alloc] init];
183             [aSource addObject:sourceName];
184             switch (fourcc) {
185                 case 'kTun':
186                     class = ITMTRemoteRadioSource;
187                     break;
188                 case 'kDev':
189                     class = ITMTRemoteGenericDeviceSource;
190                     break;
191                 case 'kPod':
192                     class = ITMTRemoteiPodSource;
193                     break;
194                 case 'kMCD':
195                 case 'kACD':
196                     class = ITMTRemoteCDSource;
197                     break;
198                 case 'kShd':
199                     class = ITMTRemoteSharedLibrarySource;
200                     break;
201                 case 'kUnk':
202                 case 'kLib':
203                 default:
204                     class = ITMTRemoteLibrarySource;
205                     break;
206             }
207             ITDebugLog(@"Adding source %@ of type %i at index %i", sourceName, class, index);
208             [aSource addObject:[NSNumber numberWithInt:class]];
209             [aSource addObject:[NSNumber numberWithInt:index]];
210             for (i = 1; i <= numPlaylists; i++) {
211                 NSString *sendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cPly'), seld:long(%u), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } } }",i,k];
212                 NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue];
213                 ITDebugLog(@" - Adding playlist %@", theObj);
214                 if (theObj) {
215                     [aSource addObject:theObj];
216                 }
217             }
218             [allSources addObject:[aSource autorelease]];
219         } else {
220             ITDebugLog(@"Source at index %i disappeared.", k);
221         }
222     }
223     ITDebugLog(@"Finished getting playlists.");
224     return [allSources autorelease];
225 }
226
227 - (NSArray *)artists
228 {
229     NSAppleEventDescriptor *rawr = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pArt'), from:obj { form:'indx', want:type('cTrk'), seld:abso($616C6C20$), from:obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:() } } } }", 'core', 'getd', &savedPSN);
230     int i;
231     NSMutableArray *array = [[NSMutableArray alloc] init];
232     NSArray *returnArray;
233     for (i = 1; i <= [rawr numberOfItems]; i++) {
234         NSString *artist = [[rawr descriptorAtIndex:i] stringValue];
235         if (artist && [artist length] && ![array containsObject:artist]) {
236             [array addObject:artist];
237         }
238     }
239     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
240     returnArray = [NSArray arrayWithArray:array];
241     [array release];
242     return returnArray;
243 }
244
245 - (NSArray *)albums
246 {
247     NSAppleEventDescriptor *rawr = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pAlb'), from:obj { form:'indx', want:type('cTrk'), seld:abso($616C6C20$), from:obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:() } } } }", 'core', 'getd', &savedPSN);
248     int i;
249     NSMutableArray *array = [[NSMutableArray alloc] init];
250     NSArray *returnArray;
251     for (i = 1; i <= [rawr numberOfItems]; i++) {
252         NSString *album = [[rawr descriptorAtIndex:i] stringValue];
253         if (album && [album length] && ![array containsObject:album]) {
254             [array addObject:album];
255         }
256     }
257     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
258     returnArray = [NSArray arrayWithArray:array];
259     [array release];
260     return returnArray;
261 }
262
263 - (int)numberOfSongsInPlaylistAtIndex:(int)index
264 {
265     int temp1;
266     ITDebugLog(@"Getting number of songs in playlist at index %i", index);
267     temp1 = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cTrk'), '----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }",index], 'core', 'getd', &savedPSN) int32Value];
268     ITDebugLog(@"Getting number of songs in playlist at index %i done", index);
269     return temp1;
270 }
271
272 - (ITMTRemotePlayerSource)currentSource
273 {
274     SInt32 fourcc;
275
276     ITDebugLog(@"Getting current source.");   
277     
278     fourcc = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value];
279     
280     switch (fourcc) {
281         case 'kTun':
282             ITDebugLog(@"Getting current source done. Source: Radio.");
283             return ITMTRemoteRadioSource;
284             break;
285         case 'kDev':
286             ITDebugLog(@"Getting current source done. Source: Generic Device.");
287             return ITMTRemoteGenericDeviceSource;
288         case 'kPod':
289             ITDebugLog(@"Getting current source done. Source: iPod.");
290             return ITMTRemoteiPodSource; //this is stupid
291             break;
292         case 'kMCD':
293         case 'kACD':
294             ITDebugLog(@"Getting current source done. Source: CD.");
295             return ITMTRemoteCDSource;
296             break;
297         case 'kShd':
298             ITDebugLog(@"Getting current source done. Source: Shared Library.");
299             return ITMTRemoteSharedLibrarySource;
300             break;
301         case 'kUnk':
302         case 'kLib':
303         default:
304             ITDebugLog(@"Getting current source done. Source: Library.");
305             return ITMTRemoteLibrarySource;
306             break;
307     }
308 }
309
310 - (int)currentSourceIndex
311 {
312     ITDebugLog(@"Getting current source.");
313     return [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value];
314 }
315
316 - (ITMTRemotePlayerPlaylistClass)currentPlaylistClass
317 {
318     SInt32 realResult;
319     ITDebugLog(@"Getting current playlist class");
320     realResult = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
321     switch (realResult)
322            {
323            case 'cLiP':
324                ITDebugLog(@"Getting current playlist class done. Class: Library.");
325                return ITMTRemotePlayerLibraryPlaylist;
326                break;
327            case 'cRTP':
328                ITDebugLog(@"Getting current playlist class done. Class: Radio.");
329                return ITMTRemotePlayerRadioPlaylist;
330                break;
331            default:
332                ITDebugLog(@"Getting current playlist class done. Class: Standard playlist.");
333                return ITMTRemotePlayerPlaylist;
334            }
335 }
336
337 - (int)currentPlaylistIndex
338 {  
339     int temp1;
340     ITDebugLog(@"Getting current playlist index.");
341     temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
342     ITDebugLog(@"Getting current playlist index done.");
343     return temp1;
344 }
345
346 - (NSString *)songTitleAtIndex:(int)index
347 {
348     NSString *temp1;
349     ITDebugLog(@"Getting song title at index %i.", index);
350     temp1 = [ITSendAEWithString([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], 'core', 'getd', &savedPSN) stringValue];
351     ITDebugLog(@"Getting song title at index %i done.", index);
352     return ( ([temp1 length]) ? temp1 : nil ) ;
353 }
354
355 - (int)currentAlbumTrackCount
356 {
357     int temp1;
358     ITDebugLog(@"Getting current album track count.");
359     temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pTrC'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
360     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
361     ITDebugLog(@"Getting current album track count done.");
362     return temp1;
363 }
364
365 - (int)currentSongTrack
366 {
367     int temp1;
368     ITDebugLog(@"Getting current song track.");
369     temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pTrN'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
370     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
371     ITDebugLog(@"Getting current song track done.");
372     return temp1;
373 }
374
375 - (NSString *)playerStateUniqueIdentifier
376 {
377     NSString *temp1;
378     ITDebugLog(@"Getting current unique identifier.");
379         NSAppleEventDescriptor *descriptor = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN);
380         if (descriptor == nil) {
381                 return nil;
382         }
383     SInt32 cls = [descriptor int32Value];
384     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
385                 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
386         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
387         if ([temp1 isEqualToString:bad]) {
388             temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
389         }
390     } else {
391         temp1 = [NSString stringWithFormat:@"%i-%i", [self currentPlaylistIndex], [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pDID'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value]];
392     }
393     ITDebugLog(@"Getting current unique identifier done.");
394     return ( ([temp1 length]) ? temp1 : nil ) ;
395 }
396
397 - (int)currentSongIndex
398 {
399     int temp1;
400     ITDebugLog(@"Getting current song index.");
401     temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
402     ITDebugLog(@"Getting current song index done.");
403     return temp1;
404 }
405
406 - (NSString *)currentSongTitle
407 {
408     NSString *temp1;
409     ITDebugLog(@"Getting current song title.");
410     
411     //If we're listening to the radio.
412     if ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] == 'cURT') {
413         NSString *bad = [NSString stringWithUTF8String:"浳湧"];
414         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
415         if ([temp1 isEqualToString:bad]) {
416             temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
417         }
418         temp1 = [temp1 stringByAppendingString:@" (Stream)"];
419     } else {
420         temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
421     }
422     ITDebugLog(@"Getting current song title done.");
423     return ( ([temp1 length]) ? temp1 : nil ) ;
424 }
425
426 - (NSString *)currentSongArtist
427 {
428     NSString *temp1;
429     ITDebugLog(@"Getting current song artist.");
430     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
431         temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pArt'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
432     } else {
433         temp1 = @"";
434     }
435     ITDebugLog(@"Getting current song artist done.");
436     return ( ([temp1 length]) ? temp1 : nil ) ;
437 }
438
439 - (NSString *)currentSongComposer
440 {
441     NSString *temp1;
442     ITDebugLog(@"Getting current song artist.");
443     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
444         temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pCmp'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
445     } else {
446         temp1 = @"";
447     }
448     ITDebugLog(@"Getting current song artist done.");
449     return ( ([temp1 length]) ? temp1 : nil ) ;
450 }
451
452 - (NSString *)currentSongAlbum
453 {
454     NSString *temp1;
455     ITDebugLog(@"Getting current song album.");
456     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
457         temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pAlb'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
458     } else {
459         temp1 = @"";
460     }
461     ITDebugLog(@"Getting current song album done.");
462     return ( ([temp1 length]) ? temp1 : nil ) ;
463 }
464
465 - (NSString *)currentSongGenre
466 {
467     NSString *temp1;
468     ITDebugLog(@"Getting current song genre.");
469     temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pGen'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
470     ITDebugLog(@"Getting current song genre done.");
471     return ( ([temp1 length]) ? temp1 : nil ) ;
472 }
473
474 - (NSString *)currentSongLength
475 {
476     SInt32 temp1;
477     NSString *temp2;
478     ITDebugLog(@"Getting current song length.");
479     temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
480     temp2 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pTim'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
481     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
482     ITDebugLog(@"Getting current song length done.");
483     return temp2;
484 }
485
486 - (NSString *)currentSongRemaining
487 {
488     SInt32 duration, current, final;
489     NSString *finalString;
490     
491     ITDebugLog(@"Getting current song remaining time.");
492     
493     duration = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pDur'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
494     current = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPos'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
495     final = duration - current;
496     finalString = [self formatTimeInSeconds:final];
497     
498     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
499     
500     ITDebugLog(@"Getting current song remaining time done.");
501     
502     return finalString;
503 }
504
505 - (NSString *)currentSongElapsed
506 {
507     long final;
508     NSString *finalString;
509     
510     ITDebugLog(@"Getting current song elapsed time.");
511         final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
512     finalString = [self formatTimeInSeconds:final];
513     ITDebugLog(@"Getting current song elapsed time done.");
514     return finalString;
515 }
516
517 - (NSImage *)currentSongAlbumArt
518 {
519     ITDebugLog(@"Getting current song album art.");
520     NSData *data = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPCT'), from:obj { form:'indx', want:type('cArt'), seld:long(1), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } } }", 'core', 'getd', &savedPSN) data];
521     ITDebugLog(@"Getting current song album art done.");    
522     if (data) {
523         return [[[NSImage alloc] initWithData:data] autorelease];
524     } else {
525         return nil;
526     }
527 }
528
529 - (int)currentSongPlayCount
530 {
531     int count;
532     ITDebugLog(@"Getting current song play count.");
533     count = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlC'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
534     ITDebugLog(@"Getting current song play count done.");
535     return count;
536 }
537
538 - (float)currentSongRating
539 {
540     float temp1;
541     ITDebugLog(@"Getting current song rating.");
542     temp1 = ((float)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRte'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] / 100.0);
543     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = -1.0; }
544     ITDebugLog(@"Getting current song rating done.");
545     return temp1;
546 }
547
548 - (BOOL)setCurrentSongRating:(float)rating
549 {
550     ITDebugLog(@"Setting current song rating to %f.", rating);
551     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
552         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pRte'), from:obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }",(long)(rating*100), [self currentSongIndex]], 'core', 'setd', &savedPSN);
553     ITDebugLog(@"Setting current song rating to %f done.", rating);
554     return YES;
555 }
556
557 - (BOOL)equalizerEnabled
558 {
559     ITDebugLog(@"Getting equalizer enabled status.");
560     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
561     ITDebugLog(@"Done getting equalizer enabled status.");
562     return (thingy != 0) ? YES : NO;
563 }
564
565 - (BOOL)setEqualizerEnabled:(BOOL)enabled
566 {
567     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
568         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
569     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
570     return YES;
571 }
572
573 - (NSArray *)eqPresets
574 {
575     int i;
576     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
577     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
578     ITDebugLog(@"Getting EQ presets");
579     for (i = 1; i <= numPresets; i++) {
580         NSString *theObj = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cEQP'), seld:long(%lu), from:'null'() } }", i], 'core', 'getd', &savedPSN) stringValue];
581         if (theObj) {
582             ITDebugLog(@"Adding preset %@", theObj);
583             [presets addObject:theObj];
584         }
585     }
586     ITDebugLog(@"Done getting EQ presets");
587     return [presets autorelease];
588 }
589
590 - (int)currentEQPresetIndex
591 {
592     int result;
593     ITDebugLog(@"Getting current EQ preset index.");
594     result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pEQP'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
595     ITDebugLog(@"Getting current EQ preset index done.");
596     return result;
597 }
598
599 - (float)volume
600 {
601     ITDebugLog(@"Getting volume.");
602     ITDebugLog(@"Getting volume done.");
603     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
604 }
605
606 - (BOOL)setVolume:(float)volume
607 {
608     ITDebugLog(@"Setting volume to %f.", volume);
609         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
610     ITDebugLog(@"Setting volume to %f done.", volume);
611     return YES;
612 }
613
614 - (BOOL)shuffleEnabled
615 {
616     ITDebugLog(@"Getting shuffle enabled status.");
617     BOOL final;
618     int result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('pPla'), seld:type('pEQP'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
619     if (result != 0) {
620         final = YES;
621     } else {
622         final = NO;
623     }
624     ITDebugLog(@"Getting shuffle enabled status done.");
625     return final;
626 }
627
628 - (BOOL)setShuffleEnabled:(BOOL)enabled
629 {
630     ITDebugLog(@"Set shuffle enabled to %i", enabled);
631         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", (unsigned long)enabled], 'core', 'setd', &savedPSN);
632     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
633     return YES;
634 }
635
636 - (ITMTRemotePlayerRepeatMode)repeatMode
637 {
638     FourCharCode m00f = 0;
639     int result = 0;
640     m00f = (FourCharCode)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('pPla'), seld:type('pEQP'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
641     ITDebugLog(@"Getting repeat mode.");
642     switch (m00f)
643     {
644         //case 'kRp0':
645         case 1800564815:
646             ITDebugLog(@"Repeat off");
647             result = ITMTRemotePlayerRepeatOff;
648             break;
649         case 'kRp1':
650             ITDebugLog(@"Repeat one");
651             result = ITMTRemotePlayerRepeatOne;
652             break;
653         case 'kRpA':
654             ITDebugLog(@"Repeat all");
655             result = ITMTRemotePlayerRepeatAll;
656             break;
657     }
658     ITDebugLog(@"Getting repeat mode done.");
659     return result;
660 }
661
662 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
663 {
664     char *m00f;
665     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
666     switch (repeatMode)
667     {
668         case ITMTRemotePlayerRepeatOne:
669             m00f = "kRp1";
670             break;
671         case ITMTRemotePlayerRepeatAll:
672             m00f = "kRpA";
673             break;
674         case ITMTRemotePlayerRepeatOff:
675         default:
676             m00f = "kRp0";
677             break;
678     }
679         ITSendAEWithString([NSString stringWithFormat:@"data:'%s', '----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:() } }", m00f], 'core', 'setd', &savedPSN);
680     ITDebugLog(@"Setting repeat mode to %c done", m00f);
681     return YES;
682 }
683
684 - (BOOL)play
685 {
686     ITDebugLog(@"Play");
687         ITSendAE('hook', 'Play', &savedPSN);
688     ITDebugLog(@"Play done");
689     return YES;
690 }
691
692 - (BOOL)pause
693 {
694     ITDebugLog(@"Pause");
695     ITSendAE('hook', 'Paus', &savedPSN);
696     ITDebugLog(@"Pause done");
697     return YES;
698 }
699
700 - (BOOL)goToNextSong
701 {
702     ITDebugLog(@"Go to next track");
703     ITSendAE('hook', 'Next', &savedPSN);
704     ITDebugLog(@"Go to next track done");
705     return YES;
706 }
707
708 - (BOOL)goToPreviousSong
709 {
710     ITDebugLog(@"Go to previous track");
711     ITSendAE('hook', 'Prev', &savedPSN);
712     ITDebugLog(@"Go to previous track done");
713     return YES;
714 }
715
716 - (BOOL)forward
717 {
718     ITDebugLog(@"Fast forward action");
719     ITSendAE('hook', 'Fast', &savedPSN);
720     ITDebugLog(@"Fast forward action done");
721     return YES;
722 }
723
724 - (BOOL)rewind
725 {
726     ITDebugLog(@"Rewind action");
727     ITSendAE('hook', 'Rwnd', &savedPSN);
728     ITDebugLog(@"Rewind action done");
729     return YES;
730 }
731
732 - (BOOL)switchToPlaylistAtIndex:(int)index
733 {
734     ITDebugLog(@"Switching to playlist at index %i", index);
735         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
736     ITDebugLog(@"Done switching to playlist at index %i", index);
737     return YES;
738 }
739
740 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
741 {
742     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
743         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from: obj { form:'indx', want:type('cSrc'), seld:long(%lu), from:'null'() } }", index - 1, index2 + 1], 'hook', 'Play', &savedPSN);
744     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
745     return YES;
746 }
747
748 - (BOOL)switchToSongAtIndex:(int)index
749 {
750     ITDebugLog(@"Switching to track at index %i", index);
751         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:() } }", index], 'hook' ,'Play', &savedPSN);
752     ITDebugLog(@"Done switching to track at index %i", index);
753     return YES;
754 }
755
756 - (BOOL)switchToEQAtIndex:(int)index
757 {
758     ITDebugLog(@"Switching to EQ preset at index %i", index);
759     // index should count from 0, but itunes counts from 1, so let's add 1.
760     [self setEqualizerEnabled:YES];
761         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pEQP'), from:'null'() }, data:obj { form:'indx', want:type('cEQP'), seld:long(%lu), from:'null'() }", (index+1)], 'core', 'setd', &savedPSN);
762     ITDebugLog(@"Done switching to EQ preset at index %i", index);
763     return YES;
764 }
765
766 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
767 {
768     int i;
769     
770     //Get fixed indexing status
771     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
772     
773     //Enabled fixed indexing
774     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
775     
776     //Search for the term
777     NSAppleEventDescriptor *searchResults = ITSendAEWithString([NSString stringWithFormat:@"pTrm:\"%@\", pAre:'%@', '----':obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:'null'() } }", term, ((type == 1) ? @"kSrR" : @"kSrL")], 'hook', 'Srch', &savedPSN);
778     
779     //If MenuTunes playlist exists
780     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
781         //Clear old MenuTunes playlist
782         int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'cnte', &savedPSN) int32Value];
783         for (i = 1; i <= numSongs; i++) {
784             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
785         }
786     } else {
787         //Create MenuTunes playlist
788         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
789     }
790     
791     //Duplicate search results to playlist
792     for (i = 1; i <= [searchResults numberOfItems]; i++) {
793         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
794     }
795     //Reset fixed indexing
796     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
797     
798     //Play MenuTunes playlist
799     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
800     
801     return YES;
802 }
803
804 - (ProcessSerialNumber)iTunesPSN
805 {
806     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
807     ProcessSerialNumber number;
808     int i;
809     int count = [apps count];
810     
811     number.highLongOfPSN = kNoProcess;
812     
813     for (i = 0; i < count; i++)
814     {
815         NSDictionary *curApp = [apps objectAtIndex:i];
816         
817         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
818         {
819             number.highLongOfPSN = [[curApp objectForKey:
820                 @"NSApplicationProcessSerialNumberHigh"] intValue];
821             number.lowLongOfPSN = [[curApp objectForKey:
822                 @"NSApplicationProcessSerialNumberLow"] intValue];
823         }
824     }
825     return number;*/
826     ProcessSerialNumber number;
827     number.highLongOfPSN = kNoProcess;
828     number.lowLongOfPSN = 0;
829     ITDebugLog(@"Getting iTunes' PSN.");
830     while ( (GetNextProcess(&number) == noErr) ) 
831     {
832         CFStringRef name;
833         if ( (CopyProcessName(&number, &name) == noErr) )
834         {
835             if ([(NSString *)name isEqualToString:@"iTunes"])
836             {
837                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
838                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
839                 ITDebugLog(@"Done getting iTunes' PSN.");
840                 return number;
841             }
842             [(NSString *)name release];
843         }
844     }
845     ITDebugLog(@"Failed getting iTunes' PSN.");
846     return number;
847 }
848
849 - (NSString*)formatTimeInSeconds:(long)seconds {
850     long final = seconds;
851     NSString *finalString;
852     if (final >= 60) {
853         if (final > 3600) {
854             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
855         } else {
856             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
857         }
858     } else {
859         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
860     }
861     return finalString;
862 }
863 - (NSString*)zeroSixty:(int)seconds {
864     if ( (seconds < 10) && (seconds > 0) ) {
865         return [NSString stringWithFormat:@"0%i",seconds];
866     } else if ( (seconds == 0) ) {
867         return [NSString stringWithFormat:@"00"];
868     } else {
869         return [NSString stringWithFormat:@"%i",seconds];
870     }
871 }
872
873 @end