#!/usr/bin/perl
# $Header: /home/matt/cvsroot/pmp3/gtk/pmp3_mini,v 1.5 2000/10/22 15:44:34 matt Exp $

use strict;
use Gtk;
use MPEG::MP3Info;
use MPEG::MP3Play qw(:msg :state);
use Getopt::Std;
use Net::FTP;
use vars qw(%config $VERSION);

$VERSION = 0.01;

sub randomize (\@);

local $| = 1;
$SIG{CHLD} = 'IGNORE';

my (@mp3_genres, %args,$start,@mp3s);

use_winamp_genres();

getopts("hrlxd:", \%args);
if(defined($args{h})) { die_usage(); }
if(!defined($args{d})) { die_usage(); }
 
$start = $args{d};
chomp $start;

if($start =~ m#^.*/$#) { chop $start; }

fork and exit;

my $mp3 = MPEG::MP3Play->new();

mp3_ls($start,\@mp3s);

unless ( defined($args{x}) ) {
    my (@dirs,@dirs_tmp);
    my $continue;

    opendir DIR,$start;
    push(@dirs, grep -d, map "$start/$_", readdir DIR);
    closedir DIR;
    foreach my $dir (@dirs) {
        unless($dir =~ /\.\.?$/) {
            opendir DIR,$dir;
            push(@dirs,grep -d, map "$dir/$_",readdir DIR);
            closedir DIR;
        	mp3_ls($dir,\@mp3s);
        }
    }
}

randomize(@mp3s) if defined $args{r};
my $count = 0;
my $paused = 0;
my $playing = 0;
my $input_tag;

my $tag = get_mp3tag($mp3s[$count]);
my $info = get_mp3info($mp3s[$count]);
my ($filename) = $mp3s[$count] =~ m#^.*/(.+?)$#;
$tag->{filename} = $filename;

my $textline;
if($tag->{TITLE} && $tag->{ARTIST}) {
	$textline = "$tag->{TITLE} - $tag->{ARTIST}";
} else {
	$textline = $tag->{filename};
}

upload_status($tag) if $config{useftp};

#########################################################################
# GTK HERE   #
##############

Gtk->init();        # initialize Gtk-Perl
Gtk->set_locale();  # internationalize

# widget creation
my $window = Gtk::Window->new( "toplevel" );

my $mainbox = Gtk::VBox->new(0,0);

my $topbuttons = Gtk::HButtonBox->new();
my $bottombuttons = Gtk::HButtonBox->new();
$window->border_width( 5 );
$window->set_default_size(203,100);
$window->set_policy(0,0,0);

$window->realize();

my %pixmaps = create_pixmaps();

my $back = Gtk::Button->new();
my $play = Gtk::Button->new();
my $next = Gtk::Button->new();
my $quit = Gtk::Button->new();
my $stop = Gtk::Button->new();
my $about = Gtk::Button->new();
my $pbar = Gtk::ProgressBar->new();
my $text = Gtk::Entry->new(66);

$pbar->set_usize(150,8);
$play->add($pixmaps{play2}); # This is an annoying fucking hack. gtk bug?
$stop->add($pixmaps{stop});
$quit->add($pixmaps{quit});
$back->add($pixmaps{back});
$next->add($pixmaps{next});
$about->add($pixmaps{q});

$bottombuttons->set_layout('spread');
$bottombuttons->set_spacing(0);
$bottombuttons->set_child_size(10,10);

$text->set_editable(0);
$text->set_text($textline);

$window->set_title('PMP3 Mini');
$window->signal_connect( "delete_event", \&quit );   
$back->signal_connect( "clicked", \&back );
$play->signal_connect( "clicked", \&play );
$next->signal_connect( "clicked", \&next );
$stop->signal_connect( "clicked", \&stop );
$quit->signal_connect("clicked", \&quit );
$about->signal_connect("clicked", \&about);

$bottombuttons->add($back);
$bottombuttons->add($play);
$bottombuttons->add($stop);
$bottombuttons->add($next);
$bottombuttons->add($quit);
$bottombuttons->add($about);

$topbuttons->add($text);

$mainbox->add($pbar);
$mainbox->add($topbuttons);
$mainbox->add($bottombuttons);

$window->add($mainbox);

$window->show_all();


# Gtk event loop
Gtk->main();


# Should never get here
exit( 0 );

#######################################################################

sub about { dialog_box("Pmp3 GTK-mini $VERSION\nby Matt Cashner\nhttp://matt.cre8tivegroup.com/pmp3"); }


sub dialog_box {
    my $msg = shift or return 0;
    my $dialog = Gtk::Dialog->new();
    $dialog->set_border_width(5);
    $dialog->set_title("About Pmp3-GTK");
    my $close = Gtk::Button->new("Close");
    $close->signal_connect("clicked",\&close,$dialog);
    $close->show();

    my $label = Gtk::Label->new($msg);
    $label->set_line_wrap(1);
    $dialog->vbox->add($label);
    $label->show();

    $dialog->action_area->pack_start($close,1,1,0);
    $dialog->set_modal(1);
    $dialog->show();
}

sub close {
    my($button,$window) = @_;
    $window->hide;
    return 0;
}

sub play {
    if($paused && $playing) {
        my($pixmap,$mask) = $pixmaps{paused}->get();
        $play->child->set($pixmap,$mask);
        $paused = 0;
        $mp3->play();
    } elsif ($playing) {
        my($pixmap,$mask) = $pixmaps{play}->get();
        $play->child->set($pixmap,$mask);
        $paused = 1;
        $mp3->pause();
    } else { 
        my($pixmap,$mask) = $pixmaps{paused}->get();
        $play->child->set($pixmap,$mask);
        $paused = 0;
        $playing = 1;
        my $input_fd = $mp3->get_command_read_pipe;
        $input_tag = Gtk::Gdk->input_add ($input_fd, 'read', \&mp3_message_handler);
    
        $tag = get_mp3tag($mp3s[$count]);
	
        ($filename) = $mp3s[$count] =~ m#^.*/(.+?)$#;
	    $tag->{filename} = $filename;

		my $textline;
		if($tag->{TITLE} && $tag->{ARTIST}) {
			$textline = "$tag->{TITLE} - $tag->{ARTIST}";
		} else {
			$textline = $tag->{filename};
		}

	    $text->set_text($textline);
    
        $mp3->open($mp3s[$count]);
        $mp3->play;
    
    	upload_status($tag) if $config{useftp};
	}
    return $mp3;
}

sub back {
    if($count == 0) {
        if($args{l}) {
            $count = $#mp3s;
        }
    } else {
        $count--;
    }
    Gtk::Gdk->input_remove($input_tag) if $playing;

    $playing = 0;
    return play();
}

sub next {
    if($count == $#mp3s) {
        $count = 0 if $args{l};
        randomize(@mp3s) if $args{r};
    } else {
        $count++;
    }
    Gtk::Gdk->input_remove($input_tag) if $playing;

    $playing = 0;
    return play();
} 

sub stop {
    $mp3->stop();
    $playing = 0;
    my($pixmap,$mask) = $pixmaps{play}->get();
    $play->child->set($pixmap,$mask);
    $pbar->update(0);
}

### Callback function to close the window
sub quit {
    $mp3->stop;
    
	if($config{useftp}) {
		open(FILE,"+>/tmp/$config{ftpfile}");
		print FILE "Pmp3-gtk not active...";
		close FILE;
		my $ftp = Net::FTP->new($config{ftphost},Passive => 1, Timeout => 10);
		$ftp->login($config{ftpuser},$config{ftppassword});
		$ftp->cwd($config{ftppath});
		$ftp->put("/tmp/$config{ftpfile}");
		$ftp->quit;
	}

    Gtk->exit(0);
    return 0;
}

# mp3_ls
# list the mp3s in a dir
sub mp3_ls {
   my $dir = shift;
   my $ar_mp3s = shift;
   opendir MP3,$dir;
   push (@$ar_mp3s,grep /^.+?\.mp3$/i,map "$dir/$_",readdir MP3);
   closedir MP3;

}

# randomize
# fisher-yeats shuffle - thanks gnat
sub randomize (\@) {
    my $arref = shift;
    my $i;
    for ($i = @$arref; --$i;) {
        my $j = int rand ($i+1);
        next if $i == $j;
        @$arref[$i,$j] = @$arref[$j,$i];
    }
}

sub mp3_message_handler {
    my $msg;
    while ( $msg = $mp3->get_message ) {

        my $code = $msg->{code};

        if ( $code == &XA_MSG_NOTIFY_INPUT_POSITION ) {
            my $percent = $msg->{position_offset}/$msg->{position_range};
            $pbar->update($percent);
        } elsif ( $code == &XA_MSG_NOTIFY_PLAYER_STATE ) {
            cleanup_and_advance() if $msg->{state} == &XA_PLAYER_STATE_EOF;
        }
    }
}

sub cleanup_and_advance {
    Gtk::Gdk->input_remove($input_tag);
    if($count == $#mp3s) {
        if($args{l}) {
            $count = 0;
			$playing = 0;
            randomize(@mp3s) if $args{r};
            return play();
        }
    } else {
        $count++;
		$playing = 0;
        return play();
    }
    return 1;
}

sub upload_status {
    eval {
        my $pid = fork();
        unless($pid) {
      	    eval { exec("upload_status '$mp3s[$count]'"); };
        }
    };
    
    if($@) {
        print "Error: $@\n";
        $config{useftp} = 0;
    }
}


BEGIN {
	eval {
		open CONFIG, "$ENV{HOME}/.pmp3rc" or die;
		while(<CONFIG>) {
			chomp;
			next if /^.*?#/;
			next unless $_;
			/^(.+?)\s+(.+?)$/;
			$config{$1} = $2;
		}
		close CONFIG;
	};
	$config{useftp} = 0 if $@;
}


sub die_usage {
   print <<EOF;

--------------------------------------------
Pmp3-gtk - The Perl MP3 Player

usage: $0 <options> -d <starting dir>
example: $0 -rlx -d /mp3/

Options:
    r: random
    l: endless loop repeat
    x: dont recurse
    d: what dir to start in (REQUIRED)
--------------------------------------------

EOF
    exit;
}

sub create_pixmaps {
    my %pixmaps;

    my @q = ( "6 6 2 1",
                        "       c None",
                        "X      c #000000000000",
                        " XXXX ",
                        "XX  XX",
                        "   XX ",
                        "  XX  ",
                        "      ",
                        "  XX  ");


    my @play2 = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              " X    ",
                              " XX   ",
                              " XXX  ",
                              " XXXX ",
                              " XXX  ",
                              " XX   ");


    my @play = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              "      ",
                              " XX   ",
                              " XXX  ",
                              " XXXX ",
                              " XXX  ",
                              " XX   ");
    
    my @stop = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              "      ",
                              " XXXX ",
                              " XXXX ",
                              " XXXX ",
                              " XXXX ",
                              " XXXX ");

    my @quit = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              "XX  XX",
                              " XXXX ",
                              "  XX  ",
                              "  XX  ",
                              " XXXX ",
                              "XX  XX");

    my @next = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              "XX   X",
                              "XXX  X",
                              "XXXX X",
                              "XXXX X",
                              "XXX  X",
                              "XX   X");


    my @back = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              "X   XX",
                              "X  XXX",
                              "X XXXX",
                              "X XXXX",
                              "X  XXX",
                              "X   XX");

    my @paused = ( "6 6 2 1",
                              "       c None",
                              "X      c #000000000000",
                              "      ",
                              "XX  XX",
                              "XX  XX",
                              "XX  XX",
                              "XX  XX",
                              "XX  XX");

    
    my ($style,$pixmap,$mask); 
    $style = $window->get_style()->bg( 'normal' );
    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @play );
    $pixmaps{play} = Gtk::Pixmap->new($pixmap, $mask);
    
    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @play2 );
    $pixmaps{play2} = Gtk::Pixmap->new($pixmap, $mask);

    
    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @stop );
    $pixmaps{stop} = Gtk::Pixmap->new($pixmap, $mask);

    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @quit );
    $pixmaps{quit} = Gtk::Pixmap->new($pixmap, $mask);

    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @next );
    $pixmaps{next} = Gtk::Pixmap->new($pixmap, $mask);

    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @back );
    $pixmaps{back} = Gtk::Pixmap->new($pixmap, $mask);

    ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                              $style,
                                                              @paused );
    $pixmaps{paused} = Gtk::Pixmap->new($pixmap, $mask);

    ( $pixmap, $mask) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window,
                                                             $style,
                                                             @q );
    $pixmaps{q} = Gtk::Pixmap->new($pixmap,$mask);

    return %pixmaps;
}

=head1 CVS INFO

    $Log: pmp3_mini,v $
    Revision 1.5  2000/10/22 15:44:34  matt
    randomizes on loopback now. and a pixmap change

    Revision 1.4  2000/10/22 04:28:28  matt
    bug fixes and improvements to gui. im pretty pleased with this client now

    Revision 1.3  2000/10/11 01:22:49  matt
    added progress bar back

    Revision 1.2  2000/10/11 00:30:53  matt
    lots of improvements. pixmaps for buttons resulting in smaller icon sizes.

    Revision 1.1  2000/10/10 23:27:23  matt
    added tiny gui ;)

=cut 
