###############################################################
######################################################################
# (c) Michael Schilli 1999
######################################################################
# Fs - File Selector Package
###############################################################
package Fs;

use Tk;
use Path;
use strict;

###############################################################
# Neuen File-Selektor definieren:
# $fs = Fs->new($topwindow, \& callback, $title);
###############################################################
sub new {
    my($type, $parentwin, $callbackref, $title) = @_;

    my $self = {};
                                            
                                            
    $self->{'callbackref'} = $callbackref;  # Parameter in
    $self->{'parentwin'}   = $parentwin;    # Instanzvariable
    $self->{'title'}       = $title;        # sichern

    bless($self, $type);
}

###############################################################
# File-Selektor anzeigen und starten: $fs->start($startdir);
###############################################################
sub start {
    my $self     = shift;
    my $startdir = shift;

    $self->{'topwin'} = $self->{'parentwin'}->Toplevel;

    $self->{'topwin'}->configure(-title => $self->{'title'});

    # Directory- und File-Listboxen
    my $listFrame = $self->{'topwin'}->Frame();
    
    $self->{'dirList'}  = 
         $listFrame->ScrlListbox(-label => "Directories");
    $self->{'fileList'} = 
         $listFrame->ScrlListbox(-label => "Files");
    
    # Entry Widget für ausgewählten Pfad/Datei
    my $fileText = 
	     $self->{'topwin'}->Entry(-textvariable => 
	                              \${$self}{'pathtext'});
    
    # Buttons
    my $buttonFrame  = $self->{'topwin'}->Frame();
    my $okButton     = $self->{'topwin'}->Button(
                         -text => "OK", 
                         -command => sub { $self->okAction });
    
    my $rescanButton = $self->{'topwin'}->Button(
                         -text => "Rescan", 
                         -command => 
                                 sub { $self->rescanAction });

    my $cancelButton = $self->{'topwin'}->Button(
                         -text => "Cancel", 
                         -command => 
                                 sub { $self->cancelAction });
    
    # Alles packen
    $listFrame->pack(-fill => "both", -expand => "yes", 
                     -side => "top");
    
    $self->{'dirList'}->pack(-fill => "both", 
			     -expand => "yes", 
			     -side => "left");

    $self->{'fileList'}->pack(-fill => "both", 
			      -expand => "yes", 
			      -side => "left");
    
    $fileText->pack(-fill => "x", -expand => "yes", 
                    -anchor => "s");
    
    $buttonFrame->pack(-fill => "x", -expand => "yes", 
                       -anchor => "s");
    $okButton->pack(-side => "left");
    $cancelButton->pack(-side => "left");
    $rescanButton->pack(-side => "left");
    
    
    # Doppel-Klick-Aktionen auf Listen definieren
    $self->{'dirList'}->bind("<Double-Button-1>" => sub { 
        $self->switch2dir($self->{'dirList'}->Getselected());
        $self->{'pathtext'} = $self->{'path'} });
    
    $self->{'fileList'}->bind("<Double-Button-1>" => 
                              sub { $self->fsexit() });
    

    # Return Key Aktion definieren
    $self->{'topwin'}->bind("<KeyPress-Return>" => 
                            sub {$self->okAction});
    
    
    # Anfangs-Pfad auf aktuelles Verzeichnis setzen
    $self->{'path'} = $startdir unless defined $self->{'path'};
    $self->switch2dir(".");
    $self->{'pathtext'} = $self->{'path'};
}
    
###############################################################
# In ein neues Verzeichnis wechseln, Listboxen aktualisieren:
# $fs->switch2dir($directory);
###############################################################
sub switch2dir {
  my $self = shift;
  my $dir  = shift;
                                    # Neuer Pfad testweise
  my $newpath = Path::cd($self->{'path'}, $dir) || return 0; 

  return 0 unless opendir(DIR, "$newpath");

  my @files = sort readdir(DIR);    # Verzeichnis lesen
  closedir(DIR);
                                    # Directory-Listbox-Update
  $self->{'dirList'}->delete(0, "end"); 
  $self->{'dirList'}->insert("end", 
                             grep(-d "$newpath/$_" , @files));
  $self->{'dirList'}->selection("set", 0);
                                    # File-Listbox-Update
  $self->{'fileList'}->delete(0, "end");
  $self->{'fileList'}->insert("end", 
                              grep(-f "$newpath/$_", @files));

  $self->{'path'} = $newpath;       # Neuen Pfad setzen
}

###############################################################
# Aktion auf das Drücken des OK-Buttons
###############################################################
sub okAction { 
    my $self = shift;

    my $item;

    if($self->{'pathtext'} ne $self->{'path'}) { 
	# Path-String manuell eingegeben
        if($item = $self->switch2dir($self->{'pathtext'})) {
	     $self->{'pathtext'} = 
	         $self->{'path'} = Path::absolute($item);
        } else {                 # Neues File ausgewählt
                                 # Dialogfenster schließen
            $self->{'topwin'}->destroy; 
                                 # Callback auslösen
            &{$self->{'callbackref'}}($self->{'pathtext'});
        }
    } elsif(($item = $self->{'dirList'}->Getselected())) { 
	# Neues Directory gewählt
        $self->switch2dir($item);
        $self->{'pathtext'} = $self->{'path'};

    } elsif($self->{'fileList'}->Getselected()) { 
	# File ausgewählt
        $self->fsexit();
    }
}

###############################################################
# Aktion auf das Drücken des Rescan-Buttons
###############################################################
sub rescanAction { 
    my $self = shift;

    my $item;

    if($self->{'pathtext'} ne $self->{'path'}) {
	# Path-String manuell eingegeben
        (($item = $self->switch2dir($self->{'pathtext'})) && 
            ($self->{'pathtext'} = 
             $self->{'path'} = Path::absolute($item))) ||
	        ($self->{'pathtext'} = $self->{'path'});    

    } elsif(($item = $self->{'dirList'}->Getselected())) { 
	# Neues Directory gewählt
        $self->switch2dir($item);
        $self->{'pathtext'} = $self->{'path'};
    } 
}
 
###############################################################
# Aktion auf das Drücken des Cancel-Buttons
###############################################################
sub cancelAction { 
    my $self = shift;

    $self->{'fileList'}->selection("clear", 0, "end");
    $self->fsexit();
}

###############################################################
# Selektiertes File mit Pfad sichern und Window entfernen
###############################################################
sub fsexit {
    my $self = shift;

    # Ausgewählten Pfad/Datei auslesen
    my $item = $self->{'fileList'}->Getselected();

    # Datei an Pfad anhängen
    $self->{'path'} =~ s,/$,,g;
    $self->{'selected'} = defined $item ? 
                                  "$self->{path}/$item" : "";

    $self->{'topwin'}->destroy;     # Dialogfenster schließen

                                    # Callbackfunktion aufrufen
    &{$self->{'callbackref'}}($self->{'selected'});  
}

###############################################################
# Ausgewähltes Verzeichnis/File abfragen
###############################################################
sub getselected {
    my $self = shift;

    $self->{'selected'};
}

1;
