/* vim: set ft=objc ts=4 nowrap: */ /* * TrackList.m * * Copyright (c) 2003 * * Author: Andreas Heppel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "TrackList.h" static TrackList *sharedTrackList = nil; @implementation TrackList - (id) init { [self initWithNibName: @"TrackList"]; } - (id) initWithNibName: (NSString *) nibName; { if (sharedTrackList) { [self dealloc]; } else { self = [super init]; if (![NSBundle loadNibNamed: nibName owner: self]) { NSLog (@"Could not load nib \"%@\".", nibName); } else { sharedTrackList = self; artist = [[NSString alloc] initWithString: _(@"Unknown")]; title = [[NSString alloc] initWithString: _(@"Unknown")]; [window setExcludedFromWindowsMenu: YES]; [titleField setStringValue: _(@"No CD")]; [window setFrameAutosaveName: @"CDTrackListWindow"]; [window setFrameUsingName: @"CDTrackListWindow"]; } } return sharedTrackList; } - (void) dealloc { RELEASE(toc); RELEASE(artist); RELEASE(title); } - (void) activate { [window makeKeyAndOrderFront: self]; } - (BOOL) isVisible { return [window isVisible]; } - (void) setTOC: (NSDictionary *) newTOC { ASSIGN(toc, newTOC); DESTROY(artist); DESTROY(title); if (!toc) { [titleField setStringValue: _(@"No CD")]; } else { artist = [[NSString alloc] initWithString: _(@"Unknown")]; title = [[NSString alloc] initWithString: _(@"Unknown")]; [titleField setStringValue: [NSString stringWithFormat: @"%@: %@", _(@"CD"), [toc objectForKey: @"cddbid"]]]; } [trackListView reloadData]; [[NSApp mainMenu] update]; } - (void) setPlaysTrack: (int) track { playsTrack = track; [trackListView reloadData]; } - (BOOL) validateMenuItem: (NSMenuItem*)item { SEL action = [item action]; // without a TOC (=> no CD) we are not going to query // a FreeDB database if (sel_eq(action, @selector(queryCddb:))) { if (!toc) return NO; } return YES; } - (NSString *)createCddbQuery: (NSDictionary *)theTOC { int i; NSArray *tracks; NSMutableString *cddbQuery; tracks = [theTOC objectForKey: @"tracks"]; cddbQuery = [NSMutableString stringWithFormat: @"%@ %d", [theTOC objectForKey: @"cddbid"], [[theTOC objectForKey: @"numberOfTracks"] intValue]]; for (i = 0; i < [tracks count]; i++) { [cddbQuery appendFormat: @" %d", [[[tracks objectAtIndex: i] objectForKey: @"offset"] intValue]]; } [cddbQuery appendFormat: @" %d", ([[theTOC objectForKey: @"discLength"] intValue] - [[[tracks objectAtIndex: 0] objectForKey: @"offset"] intValue]) / 75]; return cddbQuery; } - (void)queryCddb:(id)sender { NSString *cddbServer; Cddb *cddb = nil; if (!toc) return; cddbServer = [[NSUserDefaults standardUserDefaults] objectForKey: @"FreedbSite"]; if (cddbServer && [cddbServer length]) { int i, count; NSArray *searchPaths; NSString *bundlePath; NSBundle *bundle; Class bundleClass; // try to load the Cddb bundle searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask|NSLocalDomainMask, YES); for (i = 0; i < [searchPaths count]; i++) { bundlePath = [NSString stringWithFormat: @"%@/Bundles/Cddb.bundle", [searchPaths objectAtIndex: i]]; bundle = [NSBundle bundleWithPath: bundlePath]; if (bundle) { bundleClass = [bundle principalClass]; if (bundleClass) { cddb = [bundleClass new]; break; } else { } } else { } } // for (i = 0; i < [searchPaths count]; i++) } // if (cddbServer) { if (cddb != nil) { NSArray *matches; NSDictionary *cdInfo; NSString *queryString = [self createCddbQuery: toc]; NSArray *tracks; [cddb setDefaultSite: cddbServer]; matches = [cddb query: queryString]; if ((matches != nil) && [matches count]) { cdInfo = [cddb readWithCategory: [[matches objectAtIndex: 0] objectForKey: @"category"] discid: [[matches objectAtIndex: 0] objectForKey: @"discid"] postProcess: YES]; if (cdInfo != nil) { int i; NSString *dspTitle; ASSIGN(artist, [[cdInfo objectForKey: @"artists"] objectAtIndex: 0]); ASSIGN(title, [cdInfo objectForKey: @"album"]); dspTitle = [NSString stringWithFormat: @"%@ - %@", artist, title]; [titleField setStringValue: dspTitle]; tracks = [toc objectForKey: @"tracks"]; for (i = 0; i < [tracks count]; i++) { [[tracks objectAtIndex: i] setObject: [[cdInfo objectForKey: @"titles"] objectAtIndex: i] forKey: @"title"]; } [trackListView reloadData]; } else { // if (cdInfo != nil) NSRunAlertPanel(@"CDPlayer", _(@"Couldn't read CD information."), _(@"OK"), nil, nil); } } else { NSRunAlertPanel(@"CDPlayer", _(@"Couldn't find any matches."), _(@"OK"), nil, nil); } } else { // if (cddb != nil) NSRunAlertPanel(@"CDPlayer", _(@"Couldn't find Cddb bundle."), _(@"OK"), nil, nil); } } - (int) numberOfTracksInTOC { if (!toc) return 0; else return [[toc objectForKey: @"numberOfTracks"] intValue]; } // // NSTableView data source methods // - (int) numberOfRowsInTableView: (NSTableView *) tableView { return [self numberOfTracksInTOC]; } - (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *)tableColumn row: (int)rowIndex { NSString *identifier = [tableColumn identifier]; NSArray *tracks = [toc objectForKey: @"tracks"]; NSDictionary *track = nil; if ([identifier isEqual: @"Number"]) return [NSString stringWithFormat: @"%d", rowIndex+1]; track = [tracks objectAtIndex: rowIndex]; if ([identifier isEqual: @"Duration"]) { long min, sec, frames; long totalTime; // Frames totalTime = [[track objectForKey: @"length"] intValue]; frames = totalTime % 75; sec = totalTime / 75; min = sec / 60; sec = sec % 60; return [NSString stringWithFormat: @"%02d:%02d.%02d", min, sec, frames]; } if ([[track objectForKey: @"type"] isEqualToString: @"data"]) return [NSString stringWithFormat: _(@"%@ [Data]"), [track objectForKey: @"title"]]; else return [track objectForKey: @"title"]; } - (void) tableView: (NSTableView *)tableView willDisplayCell: (id) cell forTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex { if (rowIndex == playsTrack-1) [cell setFont: [NSFont boldSystemFontOfSize: 0]]; else [cell setFont: [NSFont systemFontOfSize: 0]]; } - (BOOL) tableView: (NSTableView *) tableView writeRows: (NSArray *) rows toPasteboard: (NSPasteboard *) pboard { int i; NSMutableDictionary *propertyList; NSMutableDictionary *cdProperties; NSMutableArray *tracks; propertyList = [[NSMutableDictionary alloc] initWithCapacity: 1]; cdProperties = [[NSMutableDictionary alloc] initWithCapacity: 3]; tracks = [[NSMutableArray alloc] initWithCapacity: [rows count]]; [cdProperties setObject: artist forKey: @"artist"]; [cdProperties setObject: title forKey: @"title"]; for (i = 0; i < [rows count]; i++) { int row; id track; NSMutableDictionary *addTrack = [NSMutableDictionary new]; row = [[rows objectAtIndex: i] intValue]; track = [[toc objectForKey: @"tracks"] objectAtIndex: row]; [addTrack setObject: [track objectForKey: @"title"] forKey: @"title"]; [addTrack setObject: [track objectForKey: @"length"] forKey: @"length"]; [addTrack setObject: [track objectForKey: @"type"] forKey: @"type"]; [addTrack setObject: [NSString stringWithFormat: @"%d", row+1] forKey: @"index"]; [tracks addObject: [addTrack autorelease]]; } // add properties for tracks and cd to proplist [cdProperties setObject: tracks forKey: @"tracks"]; [propertyList setObject: cdProperties forKey: [toc objectForKey: @"cddbid"]]; // Set property list of paste board [pboard declareTypes: [NSArray arrayWithObject: @"AudioCDPboardType"] owner: self]; [pboard setPropertyList: propertyList forType: @"AudioCDPboardType"]; RELEASE(propertyList); return YES; } - (id)validRequestorForSendType: (NSString *)sendType returnType: (NSString *)returnType { if (!returnType && [sendType isEqual: @"AudioCDPboardType"]) { if ([trackListView numberOfSelectedRows] > 0) return self; } return nil; } - (BOOL)writeSelectionToPasteboard: (NSPasteboard *)pboard types: (NSArray *)types { BOOL ret; id row; NSMutableArray *array = [NSMutableArray new]; NSEnumerator *selectedRows = [trackListView selectedRowEnumerator]; if ([types containsObject: @"AudioCDPboardType"] == NO) { return NO; } /* * Add selected rows to the array and make myself write the * corresponding track data to the pasteboard. */ while (row = [selectedRows nextObject]) { [array addObject: row]; } ret = [self tableView: trackListView writeRows: array toPasteboard: pboard]; RELEASE(array); return ret; } + (void)initialize { static BOOL initialized = NO; /* Make sure code only gets executed once. */ if (initialized == YES) return; initialized = YES; [NSApp registerServicesMenuSendTypes: [NSArray arrayWithObjects: @"AudioCDPboardType", nil] returnTypes: nil]; return; } + (id) sharedTrackList { if (sharedTrackList == nil) { sharedTrackList = [[TrackList alloc] init]; } return sharedTrackList; } @end