1/* 2 Project: MPDCon 3 4 Copyright (C) 2004 5 6 Author: Daniel Luederwald 7 8 Created: 2004-05-12 17:59:14 +0200 by flip 9 10 Collection Controller 11 12 This application is free software; you can redistribute it and/or 13 modify it under the terms of the GNU General Public 14 License as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This application is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Library General Public License for more details. 21 22 You should have received a copy of the GNU General Public 23 License along with this library; if not, write to the Free 24 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. 25*/ 26 27#include <AppKit/AppKit.h> 28#include "CollectionController.h" 29 30/* --------------------- 31 - Private Interface - 32 ---------------------*/ 33@interface CollectionController(Private) 34 35- (void) _refreshViews: (id)sender; 36- (void) _filterCollectionByString: (NSString *)filterString; 37- (void) _getAllAlbumsForArtistAt: (NSInteger)row; 38- (void) _getAllTracksForArtistAt: (NSInteger)artistRow albumAt: (NSInteger)albumRow; 39 40 41int _artistSort(id artist1, id artist2, void *context); 42int _albumSort(id album1, id album2, void *context); 43int _trackSort(id track1, id track2, void *context); 44int _titleSort(id title1, id title2, void *context); 45int _aSort(id string1, id string2, void *context); 46@end 47 48@implementation CollectionController 49 50/* -------------------------- 51 - Initialization Methods - 52 --------------------------*/ 53 54+ (id) sharedCollectionController 55{ 56 static CollectionController *_sharedCollectionController = nil; 57 58 if (! _sharedCollectionController) 59 { 60 _sharedCollectionController = [[CollectionController 61 allocWithZone: [self zone]] init]; 62 } 63 64 return _sharedCollectionController; 65} 66 67- (id) init 68{ 69 self = [self initWithWindowNibName: @"Collection"]; 70 71 if (self) 72 { 73 [self setWindowFrameAutosaveName: @"Collection"]; 74 } 75 76 return self; 77} 78 79- (void) dealloc 80{ 81 [allSongs release]; 82 [allArtists release]; 83 [allAlbums release]; 84 [filteredTracks release]; 85 [super dealloc]; 86} 87 88/* -------------------- 89 - Playlist Methods - 90 --------------------*/ 91 92- (void) addSelected: (id)sender 93{ 94 NSEnumerator *songEnum = [trackView selectedRowEnumerator]; 95 NSNumber *songNumber; 96 97 while ((songNumber = [songEnum nextObject]) != nil) 98 { 99 [[MPDController sharedMPDController] 100 addTrack: [[allSongs objectAtIndex: [songNumber intValue]] getPath]]; 101 } 102} 103 104/* --------------- 105 - Gui Methods - 106 ---------------*/ 107 108- (void) awakeFromNib 109{ 110 NSNotificationCenter *defCenter; 111 112 [trackView setAutosaveName: @"CollectionTrackTable"]; 113 [trackView setAutosaveTableColumns: YES]; 114 115 [trackView setTarget: self]; 116 [trackView setDoubleAction: @selector(doubleClicked:)]; 117 118 defCenter = [NSNotificationCenter defaultCenter]; 119 120 [defCenter addObserver: self 121 selector: @selector(didNotConnect:) 122 name: DidNotConnectNotification 123 object: nil]; 124 125 [defCenter addObserver: self 126 selector: @selector(_refreshViews:) 127 name: ShownCollectionChangedNotification 128 object: nil]; 129 130 [self _refreshViews:nil]; 131} 132 133- (void) updateCollection: (id)sender 134{ 135 [[MPDController sharedMPDController] updateCollection]; 136} 137 138- (void) doubleClicked: (id)sender 139{ 140 if ([trackView clickedRow] >= 0) 141 { 142 [self addSelected: self]; 143 } 144 else 145 { 146 NSString *identifier; 147 148 identifier = [(NSTableColumn *)[[trackView tableColumns] 149 objectAtIndex: [trackView clickedColumn]] 150 identifier]; 151 152 if ([identifier isEqual: @"artist"]) 153 { 154 NSArray *tmpArray = [[[allSongs autorelease] 155 sortedArrayUsingFunction: _artistSort 156 context: NULL] retain]; 157 allSongs = tmpArray; 158 } 159 else if ([identifier isEqual: @"album"]) 160 { 161 NSArray *tmpArray = [[[allSongs autorelease] 162 sortedArrayUsingFunction: _albumSort 163 context: NULL] retain]; 164 allSongs = tmpArray; 165 } 166 else if ([identifier isEqual: @"title"]) 167 { 168 NSArray *tmpArray = [[[allSongs autorelease] 169 sortedArrayUsingFunction: _titleSort 170 context: NULL] retain]; 171 allSongs = tmpArray; 172 } 173 else if ([identifier isEqual: @"trackNr"]) 174 { 175 NSArray *tmpArray = [[[allSongs autorelease] 176 sortedArrayUsingFunction: _trackSort 177 context: NULL] retain]; 178 allSongs = tmpArray; 179 } 180 181 [trackView reloadData]; 182 } 183} 184 185- (void) filterCollection: (id) sender 186{ 187 [self _refreshViews:nil]; 188} 189 190- (void) clearFilter: (id)sender 191{ 192 if ([[filterField stringValue] compare: @""] == NSOrderedSame) 193 { 194 return; 195 } 196 197 [filterField setStringValue: @""]; 198 [self _refreshViews:nil]; 199} 200 201/* -------------------------------- 202 - TableView dataSource Methods - 203 --------------------------------*/ 204 205- (NSInteger) numberOfRowsInTableView: (NSTableView *)tableView 206{ 207 return [allSongs count]; 208 209} 210 211- (id) tableView: (NSTableView *)tableView 212objectValueForTableColumn: (NSTableColumn *)tableColumn 213 row:(NSInteger) row 214{ 215 NSString *identifier = [tableColumn identifier]; 216 217 return [[allSongs objectAtIndex: row] valueForKey: identifier]; 218} 219 220 221/* ---------------------------- 222 - Browser delegate Methods - 223 ----------------------------*/ 224 225- (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column 226{ 227 if (column == 0) 228 { 229 return [allArtists count] + 1; 230 } 231 232 else 233 { 234 [self _getAllAlbumsForArtistAt: [sender selectedRowInColumn: 0]]; 235 return [allAlbums count]+1; 236 } 237} 238 239- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell 240 atRow:(NSInteger)row 241 column:(NSInteger)column 242{ 243#warning FIXME the formatters are not working 244 if (column == 0) { 245 [cell setLeaf: NO]; 246 247 if (row == 0) { 248 //[cell setFormatter: [[[BoldFormatter alloc] init] autorelease]]; 249 [cell setStringValue: [NSString stringWithFormat: @"All (%"PRIuPTR" Artists)", [allArtists count]]]; 250 } else { 251 //[cell setFormatter: [[[NormalFormatter alloc] init] autorelease]]; 252 [cell setStringValue: [allArtists objectAtIndex: row-1]]; 253 } 254 } else { 255 [cell setLeaf: YES]; 256 257 if (row == 0) { 258 //[cell setFormatter: [[[BoldFormatter alloc] init] autorelease]]; 259 [cell setStringValue: [NSString stringWithFormat: @"All (%"PRIuPTR" Albums)", [allAlbums count]]]; 260 } else { 261 //[cell setFormatter: [[[NormalFormatter alloc] init] autorelease]]; 262 [cell setStringValue: [allAlbums objectAtIndex: row-1]]; 263 } 264 } 265} 266 267- (NSString *)browser:(NSBrowser *)sender 268 titleOfColumn:(NSInteger)column 269{ 270 if (column == 0) { 271 return @"Artists"; 272 } else if (column == 1) { 273 return @"Albums"; 274 } else { 275 return @""; 276 } 277} 278 279 280- (void) selectionChanged: (id) sender 281{ 282 [self _getAllTracksForArtistAt: [sender selectedRowInColumn: 0] albumAt: [sender selectedRowInColumn: 1]]; 283 284 [trackView reloadData]; 285 [trackView deselectAll: sender]; 286 287 if ([trackView numberOfRows] > 0) 288 { 289 [trackView scrollRowToVisible: 0]; 290 } 291} 292 293 294/* ------------------------------ 295 - TableView dragging Methods - 296 ------------------------------*/ 297 298- (BOOL) tableView: (NSTableView *)tv 299 writeRows: (NSArray *)rows 300 toPasteboard: (NSPasteboard*)pboard 301{ 302 NSArray *typeArray; 303 NSMutableArray *songArray; 304 305 BOOL accept; 306 NSUInteger count; 307 308 count = [rows count]; 309 310 if (count > 0) 311 { 312 NSUInteger i; 313 songArray = [[NSMutableArray alloc] init]; 314 315 for (i = 0; i < count; i++) 316 { 317 [songArray addObject: [[allSongs objectAtIndex: [[rows objectAtIndex: i] integerValue]] getPath]]; 318 } 319 320 accept = YES; 321 typeArray = [NSArray arrayWithObjects: CollectionDragType, nil]; 322 [pboard declareTypes: typeArray owner: self]; 323 [pboard setPropertyList: [songArray autorelease] forType: CollectionDragType]; 324 } 325 else 326 { 327 accept = NO; 328 } 329 330 return accept; 331} 332/* ------------------------ 333 - Notification Methods - 334 ------------------------*/ 335 336- (void) didNotConnect: (NSNotification *)aNotif 337{ 338 [[self window] performClose: self]; 339} 340 341- (NSArray *) getAllTracks 342{ 343 return filteredTracks; 344} 345 346@end 347 348/* ------------------- 349 - Private Methods - 350 -------------------*/ 351@implementation CollectionController(Private) 352 353- (void) _refreshViews:(id) sender 354{ 355 [self _filterCollectionByString: [filterField stringValue]]; 356 357 [collectionView reloadColumn: 0]; 358 359 [collectionView selectRow: 0 inColumn: 0]; 360 361 [self selectionChanged: collectionView]; 362} 363 364- (void) _getAllAlbumsForArtistAt: (NSInteger)row 365{ 366 [allAlbums release]; 367 368 NSMutableSet *tmpAlbums = [[NSMutableSet alloc] init]; 369 370 NSUInteger i; 371 372 for (i = 0; i < [filteredTracks count]; i++) 373 { 374 if (row <= 0) 375 { 376 [tmpAlbums addObject: [[filteredTracks objectAtIndex: i] getAlbum]]; 377 } 378 else 379 { 380 if ([[[filteredTracks objectAtIndex: i] getArtist] compare: [allArtists objectAtIndex: (row-1)]] == NSOrderedSame) 381 { 382 [tmpAlbums addObject: [[filteredTracks objectAtIndex: i] getAlbum]]; 383 } 384 } 385 } 386 387 allAlbums = [[[[tmpAlbums autorelease] allObjects] sortedArrayUsingFunction: _aSort context: NULL] retain]; 388} 389 390 391- (void) _getAllTracksForArtistAt: (NSInteger)artistRow albumAt: (NSInteger)albumRow 392{ 393 [allSongs release]; 394 395 if (artistRow < 1) 396 { 397 if (albumRow < 1) 398 { 399 allSongs = [[NSArray arrayWithArray: filteredTracks] retain]; 400 } 401 else 402 { 403 NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; 404 405 NSUInteger i; 406 407 for (i = 0; i < [filteredTracks count]; i++) 408 { 409 if ([[[filteredTracks objectAtIndex: i] getAlbum] compare: [allAlbums objectAtIndex: (albumRow -1)]] == NSOrderedSame) 410 { 411 [tmpArray addObject: [filteredTracks objectAtIndex: i]]; 412 } 413 } 414 415 allSongs = [[NSArray arrayWithArray: [tmpArray autorelease]] retain]; 416 } 417 } 418 else 419 { 420 if (albumRow < 1) 421 { 422 NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; 423 424 NSUInteger i; 425 426 for (i = 0; i < [filteredTracks count]; i++) 427 { 428 if ([[[filteredTracks objectAtIndex: i] getArtist] compare: [allArtists objectAtIndex: (artistRow -1)]] == NSOrderedSame) 429 { 430 [tmpArray addObject: [filteredTracks objectAtIndex: i]]; 431 } 432 } 433 434 allSongs = [[NSArray arrayWithArray: [tmpArray autorelease]] retain]; 435 } 436 else 437 { 438 NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; 439 440 NSUInteger i; 441 442 for (i = 0; i < [filteredTracks count]; i++) 443 { 444 if (([[[filteredTracks objectAtIndex: i] getArtist] compare: [allArtists objectAtIndex: (artistRow -1)]] == NSOrderedSame) && 445 ([[[filteredTracks objectAtIndex: i] getAlbum] compare: [allAlbums objectAtIndex: (albumRow -1)]] == NSOrderedSame)) 446 { 447 [tmpArray addObject: [filteredTracks objectAtIndex: i]]; 448 } 449 } 450 451 allSongs = [[NSArray arrayWithArray: [tmpArray autorelease]] retain]; 452 } 453 } 454 455} 456 457- (void) _filterCollectionByString: (NSString *) fString 458{ 459 [allArtists release]; 460 [filteredTracks release]; 461 462 if ([fString compare: @""] == NSOrderedSame) 463 { 464 allArtists = [[[MPDController sharedMPDController] getAllArtists] retain]; 465 filteredTracks = [[[MPDController sharedMPDController] getAllTracksWithMetadata: YES] retain]; 466 } 467 else 468 { 469 NSArray *allTracks = [[[MPDController sharedMPDController] getAllTracksWithMetadata: YES] retain]; 470 NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; 471 NSMutableSet *tmpArtists = [[NSMutableSet alloc] init]; 472 473 NSUInteger i; 474 475 for (i = 0; i < [allTracks count]; i++) 476 { 477 if ([[[allTracks objectAtIndex: i] getArtist] rangeOfString: fString options: NSCaseInsensitiveSearch].location != NSNotFound) 478 { 479 [tmpArray addObject: [allTracks objectAtIndex: i]]; 480 } 481 else if ([[[allTracks objectAtIndex: i] getTitle] rangeOfString: fString options: NSCaseInsensitiveSearch].location != NSNotFound) 482 { 483 [tmpArray addObject: [allTracks objectAtIndex: i]]; 484 } 485 else if ([[[allTracks objectAtIndex: i] getAlbum] rangeOfString: fString options: NSCaseInsensitiveSearch].location != NSNotFound) 486 { 487 [tmpArray addObject: [allTracks objectAtIndex: i]]; 488 } 489 } 490 491 [allTracks release]; 492 filteredTracks = [[NSArray arrayWithArray: [tmpArray autorelease]] retain]; 493 494 for (i = 0; i < [filteredTracks count]; i++) 495 { 496 [tmpArtists addObject: [[filteredTracks objectAtIndex: i] getArtist]]; 497 } 498 499 allArtists = [[[[tmpArtists autorelease] allObjects] sortedArrayUsingFunction: _aSort context: NULL] retain]; 500 } 501} 502 503 504int _artistSort(id artist1, id artist2, void *context) 505{ 506 PlaylistItem *pl1, *pl2; 507 508 pl1 = (PlaylistItem *)artist1; 509 pl2 = (PlaylistItem *)artist2; 510 511 return [[pl1 getArtist] caseInsensitiveCompare: [pl2 getArtist]]; 512} 513 514int _albumSort(id album1, id album2, void *context) 515{ 516 PlaylistItem *pl1, *pl2; 517 518 pl1 = (PlaylistItem *)album1; 519 pl2 = (PlaylistItem *)album2; 520 521 return [[pl1 getAlbum] caseInsensitiveCompare: [pl2 getAlbum]]; 522} 523 524int _titleSort(id track1, id track2, void *context) 525{ 526 PlaylistItem *pl1, *pl2; 527 528 pl1 = (PlaylistItem *)track1; 529 pl2 = (PlaylistItem *)track2; 530 531 return [[pl1 getTitle] caseInsensitiveCompare: [pl2 getTitle]]; 532} 533 534int _trackSort(id track1, id track2, void *context) 535{ 536 PlaylistItem *pl1, *pl2; 537 538 pl1 = (PlaylistItem *)track1; 539 pl2 = (PlaylistItem *)track2; 540 541 return [[pl1 getTrackNr] caseInsensitiveCompare: [pl2 getTrackNr]]; 542} 543 544int _aSort(id string1, id string2, void *context) 545{ 546 return [string1 caseInsensitiveCompare: string2]; 547} 548 549@end 550 551