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