The hotkey for showing track info now calls a different method that will hide the...
[MenuTunes.git] / iTunesRemote.m
1 #import "iTunesRemote.h"
2 #import "PlaylistNode.h"
3
4 @implementation iTunesRemote
5
6 + (id)remote
7 {
8     return [[[iTunesRemote alloc] init] autorelease];
9 }
10
11 - (NSString *)remoteTitle
12 {
13     return @"iTunes Remote";
14 }
15
16 - (NSString *)remoteInformation
17 {
18     return @"Default MenuTunes plugin to control iTunes, by iThink Software.";
19 }
20
21 - (NSImage *)remoteIcon
22 {
23     return nil;
24 }
25
26 - (BOOL)begin
27 {
28     ITDebugLog(@"iTunesRemote begun");
29     savedPSN = [self iTunesPSN];
30         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationHandler:) name:@"com.apple.iTunes.playerInfo" object:@"com.apple.iTunes.player"];
31         
32         NSString *iTunesPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"];
33         NSDictionary *iTunesInfoPlist;
34         float iTunesVersion;
35
36         //Check if iTunes 5.0 or later is installed     
37         if (!iTunesPath) {
38                 iTunesPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"iTunes.app"];
39         }
40         iTunesInfoPlist = [[NSBundle bundleWithPath:iTunesPath] infoDictionary];
41         iTunesVersion = [[iTunesInfoPlist objectForKey:@"CFBundleVersion"] floatValue];
42         ITDebugLog(@"iTunes version found: %f.", iTunesVersion);
43         _iTunesVersion = iTunesVersion;
44         
45     return YES;
46 }
47
48 - (BOOL)halt
49 {
50     ITDebugLog(@"iTunesRemote halted");
51         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
52     return YES;
53 }
54
55 - (NSString *)playerFullName
56 {
57     return @"iTunes";
58 }
59
60 - (NSString *)playerSimpleName
61 {
62     return @"iTunes";
63 }
64
65 - (NSDictionary *)capabilities
66 {
67     return [NSDictionary dictionaryWithObjectsAndKeys:
68                 [NSNumber numberWithBool: YES], @"Remote",
69                 [NSNumber numberWithBool: YES], @"Basic Track Control",
70                 [NSNumber numberWithBool: YES], @"Track Information",
71                 [NSNumber numberWithBool: YES], @"Track Navigation",
72                 [NSNumber numberWithBool: YES], @"Upcoming Songs",
73                 [NSNumber numberWithBool: YES], @"Playlists",
74                 [NSNumber numberWithBool: YES], @"Volume",
75                 [NSNumber numberWithBool: YES], @"Shuffle",
76                 [NSNumber numberWithBool: YES], @"Repeat Modes",
77                 [NSNumber numberWithBool: YES], @"Equalizer",
78                 [NSNumber numberWithBool: YES], @"Track Rating",
79                 nil];
80 }
81
82 - (BOOL)showPrimaryInterface
83 {
84     ITDebugLog(@"Showing player primary interface.");
85     
86     if ([self playerRunningState] == ITMTRemotePlayerRunning) {
87         ITDebugLog(@"Showing player interface.");
88         //If not minimized and visible
89         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) &&
90                          ([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) &&
91              [[[[NSWorkspace sharedWorkspace] activeApplication] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"] ) {
92             //set minimized of browser window 1 to true
93                         ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:long(1), from:'null'() } }", 'core', 'setd', &savedPSN);
94         } else {
95             //set minimized of browser window 1 to false
96                         ITSendAEWithString(@"data:long(0), '----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:long(1), from:'null'() } }", 'core', 'setd', &savedPSN);
97         }
98         //set visible of browser window 1 to true
99                 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pvis'), from:obj { form:'indx', want:type('cBrW'), seld:long(1), from:'null'() } }", 'core', 'setd', &savedPSN);
100         //active iTunes
101                 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pisf'), from:'null'() }", 'core', 'setd', &savedPSN);
102         ITDebugLog(@"Done showing player primary interface.");
103         return YES;
104     } else {
105         NSString *path;
106         ITDebugLog(@"Launching player.");
107         if ( (path = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"]) ) {
108         } else {
109             path = [self playerFullName];
110         }
111         if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
112             ITDebugLog(@"Error Launching Player");
113             return NO;
114         }
115         return YES;
116     }
117 }
118
119 - (ITMTRemotePlayerRunningState)playerRunningState
120 {
121     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
122     int i;
123     int count = [apps count];
124     
125     for (i = 0; i < count; i++) {
126         if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
127             ITDebugLog(@"Player running state: 1");
128             return ITMTRemotePlayerRunning;
129         }
130     }
131     ITDebugLog(@"Player running state: 0");
132     return ITMTRemotePlayerNotRunning;
133 }
134
135 - (ITMTRemotePlayerPlayingState)playerPlayingState
136 {
137     SInt32 result;
138     
139     ITDebugLog(@"Getting player playing state");
140     result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlS'), from:'null'() }", 'core', 'getd', &savedPSN) typeCodeValue];
141     switch (result)
142     {
143         case 'kPSP':
144             ITDebugLog(@"Getting player playing state done. Player state: Playing");
145             return ITMTRemotePlayerPlaying;
146         case 'kPSp':
147             ITDebugLog(@"Getting player playing state done. Player state: Paused");
148             return ITMTRemotePlayerPaused;
149         case 'kPSR':
150             ITDebugLog(@"Getting player playing state done. Player state: Rewinding");
151             return ITMTRemotePlayerRewinding;
152         case 'kPSF':
153             ITDebugLog(@"Getting player playing state done. Player state: Forwarding");
154             return ITMTRemotePlayerForwarding;
155         case 'kPSS':
156         default:
157             ITDebugLog(@"Getting player playing state done. Player state: Stopped");
158             return ITMTRemotePlayerStopped;
159     }
160     ITDebugLog(@"Getting player playing state done. Player state: Stopped");
161     return ITMTRemotePlayerStopped;
162 }
163
164 /*- (NSArray *)playlists
165 {
166     long i = 0;
167     const signed long numPlaylists = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cPly'), '----':()" eventClass:@"core" eventID:@"cnte" appPSN:savedPSN];
168     NSMutableArray *playlists = [[NSMutableArray alloc] initWithCapacity:numPlaylists];
169     
170     for (i = 1; i <= numPlaylists; i++) {
171         const long j = i;
172         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];
173         NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:sendStr eventClass:@"core" eventID:@"getd" appPSN:savedPSN];
174         [playlists addObject:theObj];
175     }
176     return [playlists autorelease];
177 }*/
178
179 //Full source awareness
180 /*- (NSArray *)playlists
181 {
182     unsigned long i, k;
183     SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
184     NSMutableArray *allSources = [[NSMutableArray alloc] init];
185     
186     ITDebugLog(@"Getting playlists.");
187     if (numSources == 0) {
188                 [allSources release];
189         ITDebugLog(@"No sources.");
190         return nil;
191     }
192     
193     for (k = 1; k <= numSources ; k++) {
194         SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() }",k], 'core', 'cnte', &savedPSN) int32Value];
195         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];
196         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];
197         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];
198         unsigned long class;
199         if (sourceName) {
200             NSMutableArray *aSource = [[NSMutableArray alloc] init];
201             [aSource addObject:sourceName];
202             switch (fourcc) {
203                 case 'kTun':
204                     class = ITMTRemoteRadioSource;
205                     break;
206                 case 'kDev':
207                     class = ITMTRemoteGenericDeviceSource;
208                     break;
209                 case 'kPod':
210                     class = ITMTRemoteiPodSource;
211                     break;
212                 case 'kMCD':
213                 case 'kACD':
214                     class = ITMTRemoteCDSource;
215                     break;
216                 case 'kShd':
217                     class = ITMTRemoteSharedLibrarySource;
218                     break;
219                 case 'kUnk':
220                 case 'kLib':
221                 default:
222                     class = ITMTRemoteLibrarySource;
223                     break;
224             }
225             ITDebugLog(@"Adding source %@ of type %i at index %i", sourceName, class, index);
226             [aSource addObject:[NSNumber numberWithInt:class]];
227             [aSource addObject:[NSNumber numberWithInt:index]];
228             for (i = 1; i <= numPlaylists; i++) {
229                 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];
230                 NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue];
231                 ITDebugLog(@" - Adding playlist %@", theObj);
232                 if (theObj) {
233                     [aSource addObject:theObj];
234                 }
235             }
236             [allSources addObject:[aSource autorelease]];
237         } else {
238             ITDebugLog(@"Source at index %i disappeared.", k);
239         }
240     }
241         NSLog(@"playlists: %@", allSources);
242     ITDebugLog(@"Finished getting playlists.");
243     return [allSources autorelease];
244 }*/
245
246 - (NSArray *)playlists
247 {
248         SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
249         NSMutableArray *sources = [[NSMutableArray alloc] init];
250         int i;
251         
252         ITDebugLog(@"Getting playlists.");
253     if (numSources == 0) {
254                 [sources release];
255         ITDebugLog(@"No sources.");
256         return nil;
257     }
258         
259         //Loop through each source
260         for (i = 1; i <= numSources; i++) {
261         FourCharCode fourcc = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } }", i], 'core', 'getd', &savedPSN) typeCodeValue]; //Type of the current source
262         NSString *sourceName = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } }", i], 'core', 'getd', &savedPSN) stringValue]; //Name of the current source
263         SInt32 index = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } }", i], 'core', 'getd', &savedPSN) int32Value]; //Index of the current source
264         ITMTRemotePlayerSource class; //The class of the current source
265                 
266                 //Make a new PlaylistNode for this source
267                 PlaylistNode *sourceNode = [PlaylistNode playlistNodeWithName:sourceName type:ITMTSourceNode index:index];
268                 
269                 switch (fourcc) {
270                         case 'kTun':
271                                 class = ITMTRemoteRadioSource;
272                                 break;
273                         case 'kDev':
274                                 class = ITMTRemoteGenericDeviceSource;
275                                 break;
276                         case 'kPod':
277                                 class = ITMTRemoteiPodSource;
278                                 break;
279                         case 'kMCD':
280                         case 'kACD':
281                                 class = ITMTRemoteCDSource;
282                                 break;
283                         case 'kShd':
284                                 class = ITMTRemoteSharedLibrarySource;
285                                 break;
286                         case 'kUnk':
287                         case 'kLib':
288                         default:
289                                 class = ITMTRemoteLibrarySource;
290                                 break;
291                 }
292                 [sourceNode setSourceType:class];
293                 ITDebugLog(@"New source %@ of type %i at index %i", sourceName, class, index);
294                 
295                 int j;
296                 SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() }", i], 'core', 'cnte', &savedPSN) int32Value]; //Number of playlists in the current source
297                 
298                 //Pass 1, add all the playlists into the main array
299                 for (j = 1; j <= numPlaylists; j++) {
300                         NSString *sendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } }", j, (_iTunesVersion >= 5) ? i : index];
301                         NSString *parentSendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pPlP'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } } }", j, i];
302                         NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue], *parent = [ITSendAEWithString(parentSendStr, 'core', 'getd', &savedPSN) stringValue];
303                         ITDebugLog(@" - Adding playlist %@", theObj);
304                         if (theObj) {
305                                 FourCharCode code = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pSpK'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } }", j, i], 'core', 'getd', &savedPSN) typeCodeValue];
306                                 ITMTNodeType type;
307                                 switch (code) {
308                                         case 'kSpN':
309                                                 type = ITMTPlaylistNode;
310                                                 break;
311                                         case 'kSpF':
312                                                 type = ITMTFolderNode;
313                                                 break;
314                                         case 'kSpS':
315                                                 type = ITMTPartyShuffleNode;
316                                                 break;
317                                         case 'kSpP':
318                                                 type = ITMTPodcastsNode;
319                                                 break;
320                                         case 'kSpM':
321                                                 type = ITMTPurchasedMusicNode;
322                                                 break;
323                                         case 'kSpV':
324                                                 type = ITMTVideosNode;
325                                                 break;
326                                 }
327                                 PlaylistNode *node = [PlaylistNode playlistNodeWithName:theObj type:type index:j];
328                                 [[sourceNode children] addObject:node];
329                                 if (parent) {
330                                         int parentIndex = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pPlP'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } } }", j, i], 'core', 'getd', &savedPSN) int32Value];
331                                         [node setParent:[PlaylistNode playlistNodeWithName:parent type:ITMTFolderNode index:parentIndex]];
332                                 } else {
333                                         [node setParent:sourceNode];
334                                 }
335                         }
336                 }
337                 
338                 //Pass 2, nest each item under its proper parent. Once everything has been nested, delete the original from the main array.
339                 NSEnumerator *enumerator = [[sourceNode children] objectEnumerator];
340                 PlaylistNode *nextNode;
341                 NSMutableArray *nested = [[NSMutableArray alloc] init];
342                 
343                 while ( (nextNode = [enumerator nextObject]) ) {
344                         PlaylistNode *pNode = [nextNode parent];
345                         if ([pNode type] == ITMTFolderNode) {
346                                 PlaylistNode *newParent = nil;
347                                 int k;
348                                 for (k = 0; !newParent; k++) {
349                                         PlaylistNode *test = [[sourceNode children] objectAtIndex:k];
350                                         if ([test index] == [pNode index]) {
351                                                 newParent = test;
352                                         }
353                                 }
354                                 [[[nextNode parent] children] removeObject:nextNode];
355                                 [nextNode setParent:newParent];
356                                 [[newParent children] addObject:nextNode];
357                                 [newParent setType:ITMTFolderNode];
358                                 [nested addObject:nextNode];
359                         }
360                 }
361                 
362                 NSEnumerator *nestEnumerator = [nested objectEnumerator];
363                 while ( (nextNode = [nestEnumerator nextObject]) ) {
364                         [[sourceNode children] removeObject:nextNode];
365                         [nested removeObject:nextNode];
366                 }
367                 [nested release];
368                 
369                 //Move all the folders to the beginning of the list
370                 //Move the podcasts playlist to the top
371                 BOOL movedPodcasts = NO;
372                 enumerator = [[sourceNode children] reverseObjectEnumerator];
373                 while ( (nextNode = [enumerator nextObject]) ) {
374                         if ([nextNode type] == ITMTPodcastsNode) {
375                                 [[sourceNode children] removeObject:nextNode];
376                                 [[sourceNode children] insertObject:nextNode atIndex:1];
377                                 movedPodcasts = YES;
378                         } else if ([nextNode type] == ITMTFolderNode) {
379                                 [[sourceNode children] removeObject:nextNode];
380                                 [[sourceNode children] insertObject:nextNode atIndex:1 + movedPodcasts];
381                         }
382                 }
383                 
384                 [sources addObject:sourceNode];
385         }
386         
387         return [sources autorelease];
388 }
389
390 - (NSArray *)artists
391 {
392     NSAppleEventDescriptor *rawr = ITSendAEWithStringAndTimeout(@"'----':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, 600);
393     int i;
394     NSMutableArray *array = [[NSMutableArray alloc] init];
395     NSArray *returnArray;
396     for (i = 1; i <= [rawr numberOfItems]; i++) {
397         NSString *artist = [[rawr descriptorAtIndex:i] stringValue];
398         if (artist && [artist length] && ![array containsObject:artist]) {
399             [array addObject:artist];
400         }
401     }
402     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
403     returnArray = [NSArray arrayWithArray:array];
404     [array release];
405     return returnArray;
406 }
407
408 - (NSArray *)albums
409 {
410     NSAppleEventDescriptor *rawr = ITSendAEWithStringAndTimeout(@"'----':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, 600);
411     int i;
412     NSMutableArray *array = [[NSMutableArray alloc] init];
413     NSArray *returnArray;
414     for (i = 1; i <= [rawr numberOfItems]; i++) {
415         NSString *album = [[rawr descriptorAtIndex:i] stringValue];
416         if (album && [album length] && ![array containsObject:album]) {
417             [array addObject:album];
418         }
419     }
420     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
421     returnArray = [NSArray arrayWithArray:array];
422     [array release];
423     return returnArray;
424 }
425
426 - (int)numberOfSongsInPlaylistAtIndex:(int)index
427 {
428         /*
429                 This method only returns the proper number if there's something playing.
430                 This is because it gets the container of the current playlist so that it
431                 gets the playlist index from the current source. Operating this way is fine,
432                 since MT only ever calls this method when there is something playlist.
433                 A working version of this that works in just the main source is in the
434                 makePlaylistWithTerm:ofType: method.
435         */
436     int temp1;
437         NSAppleEventDescriptor *result;
438     ITDebugLog(@"Getting number of songs in playlist at index %i", index);
439         result = 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', 'cnte', &savedPSN);
440         temp1 = (result == nil) ? -1 : (int)[result int32Value];
441     ITDebugLog(@"Getting number of songs in playlist at index %i done", index);
442     return temp1;
443 }
444
445 - (ITMTRemotePlayerSource)currentSource
446 {
447     SInt32 fourcc;
448
449     ITDebugLog(@"Getting current source.");   
450     
451     fourcc = ([self isPlaying]) ? [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) typeCodeValue] : 'kLib';
452     
453     switch (fourcc) {
454         case 'kTun':
455             ITDebugLog(@"Getting current source done. Source: Radio.");
456             return ITMTRemoteRadioSource;
457             break;
458         case 'kDev':
459             ITDebugLog(@"Getting current source done. Source: Generic Device.");
460             return ITMTRemoteGenericDeviceSource;
461         case 'kPod':
462             ITDebugLog(@"Getting current source done. Source: iPod.");
463             return ITMTRemoteiPodSource; //this is stupid
464             break;
465         case 'kMCD':
466         case 'kACD':
467             ITDebugLog(@"Getting current source done. Source: CD.");
468             return ITMTRemoteCDSource;
469             break;
470         case 'kShd':
471             ITDebugLog(@"Getting current source done. Source: Shared Library.");
472             return ITMTRemoteSharedLibrarySource;
473             break;
474         case 'kUnk':
475         case 'kLib':
476         default:
477             ITDebugLog(@"Getting current source done. Source: Library.");
478             return ITMTRemoteLibrarySource;
479             break;
480     }
481 }
482
483 - (int)currentSourceIndex
484 {
485     ITDebugLog(@"Getting current source.");
486     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];
487 }
488
489 - (ITMTRemotePlayerPlaylistClass)currentPlaylistClass
490 {
491     FourCharCode realResult;
492     ITDebugLog(@"Getting current playlist class");
493     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) typeCodeValue];
494     switch (realResult)
495            {
496            case 'cLiP':
497                ITDebugLog(@"Getting current playlist class done. Class: Library.");
498                return ITMTRemotePlayerLibraryPlaylist;
499                break;
500            case 'cRTP':
501                ITDebugLog(@"Getting current playlist class done. Class: Radio.");
502                return ITMTRemotePlayerRadioPlaylist;
503                break;
504            default:
505                ITDebugLog(@"Getting current playlist class done. Class: Standard playlist.");
506                return ITMTRemotePlayerPlaylist;
507            }
508 }
509
510 - (int)currentPlaylistIndex
511 {  
512     int temp1;
513     ITDebugLog(@"Getting current playlist index.");
514     temp1 = ([self isPlaying] ? [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] : -1);
515     ITDebugLog(@"Getting current playlist index done.");
516     return temp1;
517 }
518
519 - (NSString *)songTitleAtIndex:(int)index
520 {
521     NSString *temp1;
522     ITDebugLog(@"Getting song title at index %i.", index);
523     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];
524     ITDebugLog(@"Getting song title at index %i done.", index);
525     return ( ([temp1 length]) ? temp1 : nil ) ;
526 }
527
528 - (BOOL)songEnabledAtIndex:(int)index
529 {
530     BOOL temp1;
531     ITDebugLog(@"Getting song enabled at index %i.", index);
532     temp1 = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('enbl'), 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) booleanValue];
533     ITDebugLog(@"Getting song enabled at index %i done.", index);
534     return temp1;
535 }
536
537 - (int)currentAlbumTrackCount
538 {
539     int temp1;
540     ITDebugLog(@"Getting current album track count.");
541     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];
542     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
543     ITDebugLog(@"Getting current album track count done.");
544     return temp1;
545 }
546
547 - (int)currentSongTrack
548 {
549     int temp1;
550     ITDebugLog(@"Getting current song track.");
551     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];
552     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
553     ITDebugLog(@"Getting current song track done.");
554     return temp1;
555 }
556
557 - (NSString *)playerStateUniqueIdentifier
558 {
559     NSString *temp1;
560     ITDebugLog(@"Getting current unique identifier.");
561         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);
562         if ([descriptor typeCodeValue] == 'prop') {
563                 return @"0-0";
564         } else if (descriptor == nil) {
565                 return nil;
566         }
567     FourCharCode cls = [descriptor typeCodeValue];
568     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
569                 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
570         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
571         if ([temp1 isEqualToString:bad]) {
572             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];
573         }
574     } else {
575         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]];
576     }
577     ITDebugLog(@"Getting current unique identifier done.");
578     return ( ([temp1 length]) ? temp1 : nil ) ;
579 }
580
581 - (int)currentSongIndex
582 {
583     int temp1;
584     ITDebugLog(@"Getting current song index.");
585         temp1 = ([self isPlaying] ? [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] : -1);
586     ITDebugLog(@"Getting current song index done.");
587     return temp1;
588 }
589
590 - (NSString *)currentSongTitle
591 {
592     NSString *temp1;
593     ITDebugLog(@"Getting current song title.");
594     FourCharCode result = [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) typeCodeValue];
595         
596     //If we're listening to the radio.
597     if (result == 'cURT') {
598         NSString *bad = [NSString stringWithUTF8String:"浳湧"];
599         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
600         if ([temp1 isEqualToString:bad]) {
601             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];
602         }
603         temp1 = [temp1 stringByAppendingString:@" (Stream)"];
604     } else if (result == 'prop') {
605                 temp1 = nil;
606         } else {
607         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];
608     }
609     ITDebugLog(@"Getting current song title done.");
610     return ( ([temp1 length]) ? temp1 : nil ) ;
611 }
612
613 - (NSString *)currentSongArtist
614 {
615     NSString *temp1;
616     ITDebugLog(@"Getting current song artist.");
617     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
618         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];
619     } else {
620         temp1 = @"";
621     }
622     ITDebugLog(@"Getting current song artist done.");
623     return ( ([temp1 length]) ? temp1 : nil ) ;
624 }
625
626 - (NSString *)currentSongComposer
627 {
628     NSString *temp1;
629     ITDebugLog(@"Getting current song artist.");
630     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
631         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];
632     } else {
633         temp1 = @"";
634     }
635     ITDebugLog(@"Getting current song artist done.");
636     return ( ([temp1 length]) ? temp1 : nil ) ;
637 }
638
639 - (NSString *)currentSongAlbum
640 {
641     NSString *temp1;
642     ITDebugLog(@"Getting current song album.");
643     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
644         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];
645     } else {
646         temp1 = @"";
647     }
648     ITDebugLog(@"Getting current song album done.");
649     return ( ([temp1 length]) ? temp1 : nil ) ;
650 }
651
652 - (NSString *)currentSongGenre
653 {
654     NSString *temp1;
655     ITDebugLog(@"Getting current song genre.");
656     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];
657     ITDebugLog(@"Getting current song genre done.");
658     return ( ([temp1 length]) ? temp1 : nil ) ;
659 }
660
661 - (NSString *)currentSongLength
662 {
663     SInt32 temp1;
664     NSString *temp2;
665     ITDebugLog(@"Getting current song length.");
666     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];
667     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];
668     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
669     ITDebugLog(@"Getting current song length done.");
670     return temp2;
671 }
672
673 - (int)currentSongDuration
674 {
675     SInt32 temp1;
676     ITDebugLog(@"Getting current song duration.");
677     temp1 = [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];
678     ITDebugLog(@"Getting current song duration done.");
679     return temp1;
680 }
681
682 - (NSString *)currentSongRemaining
683 {
684     SInt32 duration, current, final;
685     NSString *finalString;
686     
687     ITDebugLog(@"Getting current song remaining time.");
688     
689     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];
690     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];
691     final = duration - current;
692     finalString = [self formatTimeInSeconds:final];
693     
694     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
695     
696     ITDebugLog(@"Getting current song remaining time done.");
697     
698     return finalString;
699 }
700
701 - (NSString *)currentSongElapsed
702 {
703     long final;
704     NSString *finalString;
705     
706     ITDebugLog(@"Getting current song elapsed time.");
707         final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
708     finalString = [self formatTimeInSeconds:final];
709     ITDebugLog(@"Getting current song elapsed time done.");
710     return finalString;
711 }
712
713 - (NSImage *)currentSongAlbumArt
714 {
715     ITDebugLog(@"Getting current song album art.");
716     NSData *data = ([self isPlaying]) ? [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] : nil;
717     ITDebugLog(@"Getting current song album art done.");
718     if (data) {
719         return [[[NSImage alloc] initWithData:data] autorelease];
720     } else {
721         return nil;
722     }
723 }
724
725 - (int)currentSongPlayCount
726 {
727     int count;
728     ITDebugLog(@"Getting current song play count.");
729     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];
730     ITDebugLog(@"Getting current song play count done.");
731     return count;
732 }
733
734 - (float)currentSongRating
735 {
736     float temp1;
737     ITDebugLog(@"Getting current song rating.");
738     temp1 = (![self isPlaying] || ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist)) ? -1.0 : ((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);
739     ITDebugLog(@"Getting current song rating done.");
740     return temp1;
741 }
742
743 - (BOOL)setCurrentSongRating:(float)rating
744 {
745     ITDebugLog(@"Setting current song rating to %f.", rating);
746     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
747         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);
748     ITDebugLog(@"Setting current song rating to %f done.", rating);
749     return YES;
750 }
751
752 - (BOOL)currentSongShufflable
753 {
754         BOOL temp1;
755         ITDebugLog(@"Getting current song shufflable status.");
756     temp1 = (![self isPlaying] || ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist)) ? NO : [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pSfa'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) booleanValue];
757     ITDebugLog(@"Getting current song shufflable status done.");
758     return temp1;
759 }
760
761 - (BOOL)setCurrentSongShufflable:(BOOL)shufflable
762 {
763         ITDebugLog(@"Setting current song shufflable status to %i.", shufflable);
764     if ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) {
765                 ITDebugLog(@"Not a valid track to set status to, returning.");
766                 return NO;
767         }
768         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pSfa'), from:obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", shufflable, [self currentSongIndex]], 'core', 'setd', &savedPSN);
769     ITDebugLog(@"Setting current song shufflable status to %i done.", shufflable);
770     return YES;
771 }
772
773 - (BOOL)equalizerEnabled
774 {
775     ITDebugLog(@"Getting equalizer enabled status.");
776     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
777     ITDebugLog(@"Done getting equalizer enabled status.");
778     return (thingy != 0) ? YES : NO;
779 }
780
781 - (BOOL)setEqualizerEnabled:(BOOL)enabled
782 {
783     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
784         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
785     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
786     return YES;
787 }
788
789 - (NSArray *)eqPresets
790 {
791     int i;
792     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
793     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
794     ITDebugLog(@"Getting EQ presets");
795     for (i = 1; i <= numPresets; i++) {
796         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];
797         if (theObj) {
798             ITDebugLog(@"Adding preset %@", theObj);
799             [presets addObject:theObj];
800         }
801     }
802     ITDebugLog(@"Done getting EQ presets");
803     return [presets autorelease];
804 }
805
806 - (int)currentEQPresetIndex
807 {
808     int result;
809     ITDebugLog(@"Getting current EQ preset index.");
810     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];
811     ITDebugLog(@"Getting current EQ preset index done.");
812     return result;
813 }
814
815 - (float)volume
816 {
817     ITDebugLog(@"Getting volume.");
818     ITDebugLog(@"Getting volume done.");
819     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
820 }
821
822 - (BOOL)setVolume:(float)volume
823 {
824     ITDebugLog(@"Setting volume to %f.", volume);
825         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
826     ITDebugLog(@"Setting volume to %f done.", volume);
827     return YES;
828 }
829
830 - (BOOL)shuffleEnabled
831 {
832         int result;
833     ITDebugLog(@"Getting shuffle enabled status.");
834         if (![self isPlaying]) {
835                 ITDebugLog(@"No current playlist, getting shuffle status from visible playlist.");
836                 result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value];
837         } else {
838                 result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
839         }
840     ITDebugLog(@"Getting shuffle enabled status done.");
841     return (result != 0);
842 }
843
844 - (BOOL)setShuffleEnabled:(BOOL)enabled
845 {
846     ITDebugLog(@"Set shuffle enabled to %i", enabled);
847         if (![self isPlaying]) {
848                 ITDebugLog(@"No current playlist, setting shuffle status on visible playlist.");
849                 ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", (unsigned long)enabled], 'core', 'setd', &savedPSN);
850         } else {
851                 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);
852         }
853     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
854     return YES;
855 }
856
857 - (ITMTRemotePlayerRepeatMode)repeatMode
858 {
859     FourCharCode m00f = 0;
860     int result = 0;
861         ITDebugLog(@"Getting repeat mode.");
862     m00f = (FourCharCode)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) typeCodeValue];
863         
864         if (m00f == 0) {
865                 ITDebugLog(@"No current playlist, getting repeat mode from visible playlist.");
866                 m00f = (FourCharCode)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", 'core', 'getd', &savedPSN) typeCodeValue];
867         }
868         
869     switch (m00f)
870     {
871         //case 'kRp0':
872         case 1800564815:
873             ITDebugLog(@"Repeat off");
874             result = ITMTRemotePlayerRepeatOff;
875             break;
876         case 'kRp1':
877             ITDebugLog(@"Repeat one");
878             result = ITMTRemotePlayerRepeatOne;
879             break;
880         case 'kRpA':
881             ITDebugLog(@"Repeat all");
882             result = ITMTRemotePlayerRepeatAll;
883             break;
884     }
885     ITDebugLog(@"Getting repeat mode done.");
886     return result;
887 }
888
889 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
890 {
891     char *m00f;
892     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
893     switch (repeatMode)
894     {
895         case ITMTRemotePlayerRepeatOne:
896             m00f = "kRp1";
897             break;
898         case ITMTRemotePlayerRepeatAll:
899             m00f = "kRpA";
900             break;
901         case ITMTRemotePlayerRepeatOff:
902         default:
903             m00f = "kRp0";
904             break;
905     }
906         if (![self isPlaying]) {
907                 ITDebugLog(@"No current playlist, setting repeat mode on visible playlist.");
908                 ITSendAEWithString([NSString stringWithFormat:@"data:'%s', '----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", m00f], 'core', 'setd', &savedPSN);
909         } else {
910                 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);
911         }
912     ITDebugLog(@"Setting repeat mode to %c done", m00f);
913     return YES;
914 }
915
916 - (BOOL)play
917 {
918     ITDebugLog(@"Play");
919         ITSendAE('hook', 'Play', &savedPSN);
920     ITDebugLog(@"Play done");
921     return YES;
922 }
923
924 - (BOOL)pause
925 {
926     ITDebugLog(@"Pause");
927     ITSendAE('hook', 'Paus', &savedPSN);
928     ITDebugLog(@"Pause done");
929     return YES;
930 }
931
932 - (BOOL)goToNextSong
933 {
934     ITDebugLog(@"Go to next track");
935     ITSendAE('hook', 'Next', &savedPSN);
936     ITDebugLog(@"Go to next track done");
937     return YES;
938 }
939
940 - (BOOL)goToPreviousSong
941 {
942     ITDebugLog(@"Go to previous track");
943     ITSendAE('hook', 'Back', &savedPSN);
944     ITDebugLog(@"Go to previous track done");
945     return YES;
946 }
947
948 - (BOOL)forward
949 {
950     ITDebugLog(@"Fast forward action");
951     ITSendAE('hook', 'Fast', &savedPSN);
952     ITDebugLog(@"Fast forward action done");
953     return YES;
954 }
955
956 - (BOOL)rewind
957 {
958     ITDebugLog(@"Rewind action");
959     ITSendAE('hook', 'Rwnd', &savedPSN);
960     ITDebugLog(@"Rewind action done");
961     return YES;
962 }
963
964 - (BOOL)switchToPlaylistAtIndex:(int)index
965 {
966     ITDebugLog(@"Switching to playlist at index %i", index);
967         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
968     ITDebugLog(@"Done switching to playlist at index %i", index);
969     return YES;
970 }
971
972 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
973 {
974     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
975         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);
976     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
977     return YES;
978 }
979
980 - (BOOL)switchToSongAtIndex:(int)index
981 {
982     ITDebugLog(@"Switching to track at index %i", index);
983         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);
984     ITDebugLog(@"Done switching to track at index %i", index);
985     return YES;
986 }
987
988 - (BOOL)switchToEQAtIndex:(int)index
989 {
990     ITDebugLog(@"Switching to EQ preset at index %i", index);
991     // index should count from 0, but itunes counts from 1, so let's add 1.
992     [self setEqualizerEnabled:YES];
993         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);
994     ITDebugLog(@"Done switching to EQ preset at index %i", index);
995     return YES;
996 }
997
998 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
999 {
1000     int i;
1001         
1002     //Get fixed indexing status
1003     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
1004     
1005     //Enabled fixed indexing
1006     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
1007     
1008     //Search for the term
1009     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);
1010     
1011     //If MenuTunes playlist exists
1012     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
1013         //Clear old MenuTunes playlist
1014                 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
1015         for (i = 1; i <= numSongs; i++) {
1016             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
1017         }
1018     } else {
1019         //Create MenuTunes playlist
1020         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
1021     }
1022     
1023     //Duplicate search results to playlist
1024     for (i = 1; i <= [searchResults numberOfItems]; i++) {
1025                 //NSLog(@"%@", ITSendAEWithStringAndParameter(@"'----':obj { form:'prop', want:type('prop'), seld:prop('pnam'), from:aevt(@) }", *[[searchResults descriptorAtIndex:i] aeDesc], 'core', 'getd', &savedPSN));
1026                 
1027         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
1028     }
1029     //Reset fixed indexing
1030     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
1031     
1032     //Play MenuTunes playlist
1033     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
1034     
1035     return YES;
1036 }
1037
1038 - (BOOL)isPlaying
1039 {
1040         return ([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) typeCodeValue] != 'prop');
1041 }
1042
1043 - (void)notificationHandler:(NSNotification *)note
1044 {
1045         ITDebugLog(@"Received notification: %@", note);
1046         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTTrackChanged" object:self userInfo:[note userInfo]];
1047         ITDebugLog(@"Handled notification.");
1048 }
1049
1050 - (ProcessSerialNumber)iTunesPSN
1051 {
1052     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
1053     ProcessSerialNumber number;
1054     int i;
1055     int count = [apps count];
1056     
1057     number.highLongOfPSN = kNoProcess;
1058     
1059     for (i = 0; i < count; i++)
1060     {
1061         NSDictionary *curApp = [apps objectAtIndex:i];
1062         
1063         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
1064         {
1065             number.highLongOfPSN = [[curApp objectForKey:
1066                 @"NSApplicationProcessSerialNumberHigh"] intValue];
1067             number.lowLongOfPSN = [[curApp objectForKey:
1068                 @"NSApplicationProcessSerialNumberLow"] intValue];
1069         }
1070     }
1071     return number;*/
1072     ProcessSerialNumber number;
1073     number.highLongOfPSN = kNoProcess;
1074     number.lowLongOfPSN = 0;
1075     ITDebugLog(@"Getting iTunes' PSN.");
1076     while ( (GetNextProcess(&number) == noErr) ) 
1077     {
1078         CFStringRef name;
1079         if ( (CopyProcessName(&number, &name) == noErr) )
1080         {
1081             if ([(NSString *)name isEqualToString:@"iTunes"])
1082             {
1083                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
1084                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
1085                 ITDebugLog(@"Done getting iTunes' PSN.");
1086                                 [(NSString *)name release];
1087                 return number;
1088             }
1089             [(NSString *)name release];
1090         }
1091     }
1092     ITDebugLog(@"Failed getting iTunes' PSN.");
1093     return number;
1094 }
1095
1096 - (NSString*)formatTimeInSeconds:(long)seconds {
1097     long final = seconds;
1098     NSString *finalString;
1099     if (final >= 60) {
1100         if (final > 3600) {
1101             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
1102         } else {
1103             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
1104         }
1105     } else {
1106         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
1107     }
1108     return finalString;
1109 }
1110 - (NSString*)zeroSixty:(int)seconds {
1111     if ( (seconds < 10) && (seconds > 0) ) {
1112         return [NSString stringWithFormat:@"0%i",seconds];
1113     } else if ( (seconds == 0) ) {
1114         return [NSString stringWithFormat:@"00"];
1115     } else {
1116         return [NSString stringWithFormat:@"%i",seconds];
1117     }
1118 }
1119
1120 @end