package iPodDB::Songlist; =head1 NAME iPodDB::Songlist - listing of songs in a playlist =head1 SYNOPSIS my $songlist = iPodDB::Songlist->new( $frame ); $songlist->populate( $playlist ); =head1 DESCRIPTION This module provides a listing of the songs in an iPod playlist. It has a few events attached to it such as: sorting the column on click, and poping up the File menu when you right-click a row. =cut use base qw( Wx::ListCtrl Exporter Class::Accessor ); use Wx qw( wxLC_REPORT wxLC_VRULES wxLC_HRULES wxLIST_STATE_SELECTED wxLIST_NEXT_ALL ); use Wx::Event qw( EVT_LIST_COL_CLICK EVT_LIST_ITEM_RIGHT_CLICK EVT_LIST_ITEM_SELECTED EVT_LIST_ITEM_DESELECTED EVT_LIST_BEGIN_DRAG ); use Wx::DND; use strict; use warnings; use Path::Class; use constant ARTIST => 0; use constant ALBUM => 1; use constant TITLE => 2; use constant ASCENDING => 0; use constant DESCENDING => 1; our $VERSION = '0.02'; our @EXPORT_OK = qw( song_to_path ); my @columns = qw( artist album title ); my @column_sort = ( ASCENDING ) x 3; my $current_sort = ARTIST; =head1 PROPERTIES =head2 songs An list of Mac::iPod::DB::Song objects =cut __PACKAGE__->mk_accessors( qw( songs ) ); =head1 METHODS =head2 new( $frame ) This creates the list widget. =cut sub new { my $class = shift; my $parent = shift; my $self = $class->SUPER::new( $parent, -1, [ -1, -1 ], [ -1, -1 ], wxLC_REPORT | wxLC_VRULES | wxLC_HRULES ); bless $self, $class; $self->InsertColumn( $_, ucfirst( $columns[ $_ ] ) ) for 0..$#columns; $self->EVT_LIST_COL_CLICK( $self, \&on_column_click ); $self->EVT_LIST_ITEM_RIGHT_CLICK( $self, \&on_row_right_click ); $self->EVT_LIST_ITEM_SELECTED( $self, \&on_select ); $self->EVT_LIST_ITEM_DESELECTED( $self, \&on_select ); $self->EVT_LIST_BEGIN_DRAG( $self, \&on_drag ); return $self; } =head2 populate( @songs ) This will add the list of songs to the listing as well as update the status bar. =cut sub populate { my $self = shift; my @songs = @_; my $status = $self->GetGrandParent->status; $self->DeleteAllItems; $status->clear; return unless @songs; my $time; my $filesize; for my $index ( 0...$#songs ) { my $id = $self->InsertItem( Wx::ListItem->new ); my $song = $songs[ $index ]; for( 0..$#columns ) { my $column = $columns[ $_ ]; $self->SetItem( $id, $_, $song->$column ); } $self->SetItemData( $id, $index ); $time += $song->time; $filesize += $song->filesize; } $self->songs( \@songs ); $status->songs( scalar @songs ); $status->time( $time ); $status->size( $filesize ); $self->SetColumnWidth( $_, -1 ) for 0..$#columns; $self->SortItems( sub { return $self->cmp_songs( $current_sort, @_ ); } ); } =head2 as_songobject( [ @items ] ) This function returns an array of Mac::iPod::DB::Song objects based on a list of items. If no list is provided the currently selected items are used. =cut sub as_songobject { my $self = shift; my @items = @_; my @allsongs = @{ $self->songs }; my $item = -1; my @songs; unless( @items ) { while( ( $item = $self->GetNextItem( $item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ) ) != -1 ) { push @items, $self->GetItemData( $item ); } } push @songs, $allsongs[ $_ ] for @items; return @songs; } =head2 as_filedataobject( [ @items ] ) Return a Wx::FileDataObject from a list of items suitable for sending to the clipboard or for processing in a drag and drop event. If no list is provided, the currently selected items are used. =cut sub as_filedataobject { my $self = shift; my @items = @_; my $path = dir( $self->GetGrandParent->preferences->mountpoint ); my $files = Wx::FileDataObject->new; for my $song ( $self->as_songobject( @items ) ) { my $file = song_to_path( $path, $song ); $files->AddFile( $file->stringify ); } return $files; } =head2 cmp_songs( $column, $a, $b ) This is the sorting function. =cut sub cmp_songs { my $self = shift; my $column = shift; my $a = shift; my $b = shift; my @songs = @{ $self->songs }; my $field = $columns[ $column ]; return 0 unless @songs; if( $column_sort[ $column ] == DESCENDING ) { my $temp = $a; $a = $b; $b = $temp; } return $songs[ $a ]->$field cmp $songs[ $b ]->$field; } =head2 song_to_path( $directory, $song ) This utility function takes a directory (Path::Class object) and then the a song object. It then translates the stored path to a path relative to the $directory. This method is available for export. =cut sub song_to_path { my $dir = shift; my @path = split( ':', shift->path ); for( 1..$#path ) { $dir = $_ == $#path ? $dir->file( $path[ $_ ] ) : $dir->subdir( $path[ $_ ] ); } return $dir; } =head1 EVENTS =head2 on_column_click( ) This event will sort the listing based on which column was clicked. It will flip between ascending order and descending order on every click. =cut sub on_column_click { my $self = shift; my $event = shift; my $column = $event->GetColumn; if( $current_sort == $column ) { $column_sort[ $column ] ^= 1; } else { $current_sort = $column; } $self->SortItems( sub { return $self->cmp_songs( $column, @_ ); } ); } =head2 on_row_right_click( ) This event will pop up the File menu as long as at least one song is selected. =cut sub on_row_right_click { my $self = shift; my $event = shift; my $parent = $self->GetGrandParent; if( $self->GetSelectedItemCount ) { $parent->PopupMenu( iPodDB::Menu::File->new( $parent ), $event->GetPoint ); } } =head2 on_select( ) This event enables or disables options on the File menu when items are selected or deselected. =cut sub on_select { my $self = shift; my $menu = $self->GetGrandParent->menu; my $enable = $self->GetSelectedItemCount ? 1 : 0; my @menus = ( 'File', 'Copy To...', 'File', 'Copy' ); while( @menus ) { my $item = $menu->FindMenuItem( shift( @menus ), shift( @menus ) ); $menu->Enable( $item, $enable ); } } =head2 on_drag() This event allows a user to drag files to a destination. =cut sub on_drag { my $self = shift; return unless $self->GetSelectedItemCount; Wx::DropSource->new( $self->as_filedataobject, $self->GetGrandParent )->DoDragDrop; } =head1 AUTHOR =over 4 =item * Brian Cassidy Ebricas@cpan.orgE =back =head1 COPYRIGHT AND LICENSE Copyright 2004 by Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;