The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
# gSequence - Protein Sequence Control Panel
# by Lorenz Pollsk
#
# this is work in progress! use this only for testing

use Gtk;
use strict;
use Bio::Seq;
use Bio::SeqIO;
use Bio::Tools::SeqStats;
use Bio::SeqFeature::Generic;
use Bio::Index::Abstract;
use Bio::DB::GenBank;
use Bio::DB::GenPept;

init Gtk;

# constant
my $false = 0;
my $true = 1;

# global widgets
my ($main_notebook,@main_label,@seq_edit);
my $about_dialog;
my ($import_dialog,$import_entry,@import_buttons,$import_from);
my ($description_window,$description_edit);
my ($comment_window,$comment_edit,$current_comment,$comment_frame);
my ($seqstats_window,$seqstats_edit);
my ($dblink_window,@dblink_entry,$current_dblink,$dblink_clist,$dblink_handler_id);
my ($ref_window,@ref_entry,$current_ref,$ref_clist,$ref_handler_id);
my ($feature_window,@feature_entry,$current_feature_item,@feature_spinner,
    $feature_handler_id,$feature_tree);
my ($pref_window,@pref_entry);

# global file data
my @seq;
my @filename;
my @modified;
my @locked; # locked sequence for editing ?
my $current;

# menu
my @menu_items = ( { path        => '/_File',
		     type        => '<Branch>' },
		   { path        => '/File/_New',
		     accelerator => '<control>N',
		     callback    => \&new },
		   { path        => '/File/_Open SwissProt',
		     accelerator => '<control>O',
		     callback    => \&open_dialog },
		   { path        => '/File/_Save SwissProt',
		     accelerator => '<control>S',
		     callback    => \&save },
		   { path        => '/File/Save _As...',
		     callback    => \&saveas_dialog },
		   { path        => '/File/Close',
		     callback    => \&close },
		   { path        => '/File/sep1',
		     type        => '<Separator>' },
		   { path        => '/File/_Import from...',
		     type        => '<Branch>' },
                   { path        => '/File/Import from.../Remote DB',
		     type        => '<Branch>' },
                   { path        => '/File/Import from.../Remote DB/AceDB',
		     callback    => sub { &seq_import("ace"); } },
                   { path        => '/File/Import from.../Remote DB/GenPept',
		     callback    => sub { &seq_import("genpept"); } },
                   { path        => '/File/Import from.../Flat File Index',
		     type        => '<Branch>' },
                   { path        => '/File/Import from.../Flat File Index/Fasta',
		     callback    => sub { &seq_import("fasta"); } },
                   { path        => '/File/Import from.../Flat File Index/SwissProt',
		     callback    => sub { &seq_import("swissprot"); } },
                   { path        => '/File/Import from.../Flat File Index/SwissPfam',
		     callback    => sub { &seq_import("swisspfam"); } },
		   { path        => '/File/_Export to...' },
		   { path        => '/File/sep2',
		     type        => '<Separator>' },
		   { path        => '/File/_Quit',
		     callback    => sub { Gtk->exit( 0 ); } },

		   { path        => '/_Edit',
		     type        => '<Branch>' },
		   { path        => '/Edit/C_ut',
		     callback    => sub { $seq_edit[$current]->cut_clipboard(); },
		     accelerator => '<control>X' },
		   { path        => '/Edit/_Copy',
		     callback    => sub { $seq_edit[$current]->copy_clipboard(); },
		     accelerator => '<control>C' },
		   { path        => '/Edit/_Paste',
		     callback    => sub { $seq_edit[$current]->paste_clipboard(); },
		     accelerator => '<control>V' },
		   { path        => '/Edit/Select All',
		     callback    => sub { $seq_edit[$current]->select_region(0,-1); } },

		   { path        => '/_Specs',
		     type        => '<Branch>' },
		   { path        => '/Specs/_Sequence Stats',
		     callback    => sub {&update_seqstats_window(1);} },
		   { path        => '/Specs/sep1',
		     type        => '<Separator>' },
		   { path        => '/Specs/_Description',
		     callback    => sub {&update_description_window(1);} },
		   { path        => '/Specs/_Comments',
		     callback    => sub {&update_comment_window(1);} },
		   { path        => '/Specs/_DB Links',
		     callback    => sub {&update_dblink_window(1);} },
		   { path        => '/Specs/_References',
		     callback    => sub {&update_reference_window(1);} },
		   { path        => '/Specs/sep2',
		     type        => '<Separator>' },
		   { path        => '/Specs/_Features',
		     callback    => sub {&update_feature_window(1);} },

		   { path        => '/_Tools',
		     type        => '<Branch>' },
		   { path        => '/Tools/Code Table' },
		   { path        => '/Tools/sep1',
		     type        => '<Separator>' },
		   { path        => '/Tools/local Blast' },
		   { path        => '/Tools/local HMMER' },
		   { path        => '/Tools/hmmpfam' },
		   { path        => '/Tools/web Blast' },

		   { path        => '/_Options',
		     type        => '<Branch>' },
		   { path        => '/Options/_Preferences',
		     callback    => sub {&update_pref_window(1);} },

		   { path        => '/_Help',
		     type        => '<LastBranch>' },
		   { path        => '/Help/Help' },
		   { path        => '/Help/_About...',
		     callback    => sub { $about_dialog->show_all();} } );

### main

$current = 0;
&init_windows();
main Gtk;
exit( 0 );


### Subroutines

sub init_windows
{
    &init_main_window();
    &init_about_dialog();
    &init_import_dialog();
    &init_seqstats_window();
    &init_description_window();
    &init_comment_window();
    &init_dblink_window();
    &init_reference_window();
    &init_feature_window();
    &init_pref_window();
}

sub init_main_window
{
    # toplevel window
    my $window;
    $window = new Gtk::Window( 'toplevel' );
    $window->signal_connect( 'destroy', sub { Gtk->exit( 0 ); } );
    $window->set_title( "gSequence" );
    $window->set_usize( 600, 400 );

    # vertical box containing menu and text editor widget
    my $main_vbox;
    $main_vbox = new Gtk::VBox( $false, 1 );
    $main_vbox->border_width( 1 );
    $window->add( $main_vbox );

    # handlebox for menubar
    my $handlebox;
    $handlebox = new Gtk::HandleBox();
    $main_vbox->pack_start( $handlebox, $false, $true, 0 );

    # menubar
    my $menubar;
    $menubar = get_menu( $window );
    $handlebox->add( $menubar );

    # text widget
    $seq_edit[$current] = new Gtk::Text( undef, undef );
    $seq_edit[$current]->set_editable( $true );

    # vertical scrollbar for text widget
    my $scrollbar;
    $scrollbar = new Gtk::VScrollbar( $seq_edit[$current]->vadj );

    # horizontal box containing text widget and scrollbar
    my $seq_edit_hbox;
    $seq_edit_hbox = new Gtk::HBox( $false, 1 );
    $seq_edit_hbox->border_width( 1 );
    $seq_edit_hbox->pack_start( $seq_edit[$current], $true, $true, 0);
    $seq_edit_hbox->pack_end( $scrollbar, $false, $true, 0);

    $main_notebook = new Gtk::Notebook();
    $main_notebook->set_tab_pos( 'top' );

    $main_vbox->pack_end( $main_notebook, $true, $true, 0);

    # show everything
    $window->show_all();

    $main_notebook->signal_connect_after("switch-page",
       sub{ #$seq[$current]->seq($seq_edit[$current]->get_chars(0,-1)) 
	    #   if (defined($seq[$current]));
	    $current = $main_notebook->get_current_page(); 
	    &update_seq_data(); } );
}

sub get_menu
{
    my ( $window ) = @_;
    
    my $menubar;
    my $item_factory;
    my $accel_group;

    $accel_group = new Gtk::AccelGroup();

    # This function initializes the item factory.
    # Param 1: The type of menu - can be 'Gtk::MenuBar', 'Gtk::Menu',
    #          or 'Gtk::OptionMenu'.
    # Param 2: The path of the menu.
    # Param 3: The accelerator group.  The item factory sets up
    #          the accelerator table while generating menus.
    $item_factory = new Gtk::ItemFactory( 'Gtk::MenuBar',
					  '<main>',
					  $accel_group );

    # This function generates the menu items. Pass the item factory,
    # the number of items in the array, the array itself, and any
    # callback data for the the menu items.
    $item_factory->create_items( @menu_items );

    # Attach the new accelerator group to the window.
    $window->add_accel_group( $accel_group );

    # Finally, return the actual menu bar created by the item factory.
    #*menubar = gtk_item_factory_get_widget (item_factory, "&lt;main>");
    return ( $item_factory->get_widget( '<main>' ) );
}

sub new_seq_page
{
    my ($seq) = shift;
    my $curr;

    push @seq,$seq;
    $curr = @seq - 1;
    $main_label[$curr] = new Gtk::Label($seq[$curr]->id())
	if (defined($seq[$curr]));
    $main_label[$curr] = new Gtk::Label("<New>")
	if (!defined($seq[$curr]));

    # text widget
    $seq_edit[$curr] = new Gtk::Text( undef, undef );
    $seq_edit[$curr]->set_editable( $true );

    # vertical scrollbar for text widget
    my $scrollbar;
    $scrollbar = new Gtk::VScrollbar( $seq_edit[$curr]->vadj );

    # horizontal box containing text widget and scrollbar
    my $seq_edit_hbox;
    $seq_edit_hbox = new Gtk::HBox( $false, 1 );
    $seq_edit_hbox->border_width( 1 );
    $seq_edit_hbox->pack_start( $seq_edit[$curr], $true, $true, 0);
    $seq_edit_hbox->pack_end( $scrollbar, $false, $true, 0);

    $main_notebook->append_page( $seq_edit_hbox, $main_label[$curr] );
    $main_notebook->show_all();
    $main_notebook->set_page(-1);
}

sub seq_fetch
{
    my ($server,$port,$dir,$db); # read from preferences
    my ($dbobj);

    return if (!defined($import_from) || !($import_from));

    $dbobj = Bio::DB::GenPept->new() if ($import_from eq "genpept");
    $dbobj = Bio::DB::Ace->new(-host=>$server,-port=>$port)
	if ($import_from eq "ace");
    $dbobj = Bio::Index::Abstract->new("$dir/$db") 
	if ($import_from eq "fasta") ||
	   ($import_from eq "swissprot") ||
	   ($import_from eq "swisspfam");

    if( $import_buttons[0]->get_active() ) {
	&new_seq_page($dbobj->get_Seq_by_id($import_entry->get_text()));
    } else {
	&new_seq_page($dbobj->get_Seq_by_acc($import_entry->get_text()));
    }
}

sub seq_import
{
    ($import_from) = @_;
    my %names = ( "ace" => "AceDB",
			 "genpept" => "GenPept DB",
			 "fasta" => "Fasta Flat File",
			 "swissprot" => "SwissProt Flat File",
			 "swisspfam" => "SwissPfam Flat File"
		     );
    $import_dialog->set_title("Import from ".$names{$import_from});
    $import_entry->set_text("");
    $import_dialog->show_all();
}

sub init_import_dialog
{
    $import_dialog = new Gtk::Dialog();
    $import_dialog->border_width(5);

    # create the first button and add it to a box
    my $button = new Gtk::RadioButton( "Fetch by ID" );
    $import_dialog->vbox->pack_start($button,$false,$false,2);
         
    # create the second button and add it to a box
    $button = new Gtk::RadioButton( "Fetch by ACCESSION", $button );
    $import_dialog->vbox->pack_start($button,$false,$false,2);
    @import_buttons = $button->group();

    $import_entry = new Gtk::Entry();
    my $frame = new Gtk::Frame("Enter here:");
    $frame->add($import_entry);
    $import_dialog->vbox->pack_start( $frame, $true, $true, 5);
    
    my $bbox = new Gtk::HButtonBox();
    $bbox->set_layout("end");
    
    $button = new Gtk::Button( "OK" );
    $bbox->add( $button );
    $button->signal_connect("clicked",
           # OK button handler
           sub{ $import_dialog->hide();
		&seq_fetch();
	      });

    $button = new Gtk::Button( "Cancel" );
    $bbox->add( $button );
    $button->signal_connect("clicked",
           # close button handler
           sub{ $import_dialog->hide();
	      });

    $import_dialog->action_area->pack_start( $bbox, $true, $true, 0 );

    $import_dialog->signal_connect_after( "delete_event",
           # window delete handler
           sub{ $import_dialog->hide();
                return &Gtk::true;
	      });
}

sub open_dialog
{
    # Create a new file selection widget
    my $open_dialog = new Gtk::FileSelection( "Open File..." );
    # Connect the ok_button to open_ok_sel function
    $open_dialog->ok_button->signal_connect( "clicked",
					     \&ok_open_dialog,
					     $open_dialog );
    # Connect the cancel_button to destroy the widget
    $open_dialog->cancel_button->signal_connect( "clicked",
						 sub { $open_dialog->destroy(); } );
    $open_dialog->show();
}

# Get the selected filename
sub ok_open_dialog
  {
    my ( $widget, $file_selection ) = @_;
    push @filename, $file_selection->get_filename();

    $widget->parent->parent->parent->destroy();

    my $in = Bio::SeqIO->new(-file => $filename[-1] , '-format' => 'swiss');

    &new_seq_page($in->next_seq());
}

sub update_seq_data
{    
    $main_label[$current]->set_text($seq[$current]->id) if (defined($seq[$current]));
    $main_label[$current]->set_text("<New>") if (!defined($seq[$current]));

    $seq_edit[$current]->freeze();
    $seq_edit[$current]->delete_text(0,-1);
    $seq_edit[$current]->insert(undef,undef,undef,$seq[$current]->seq()) if (defined($seq[$current]));
    $seq_edit[$current]->thaw();

    &update_comment_window();
    &update_description_window();
    &update_seqstats_window();
    &update_dblink_window();
    &update_reference_window();
    &update_feature_window();
}

sub new
{
    &new_seq_page(undef);
}

sub close
{
}

sub save
{
    if (!defined($filename[$current])||!$filename[$current])
    {
	&saveas_dialog;
	return;
    }
    my $out = Bio::SeqIO->new(-file => ">$filename[$current]" , '-format' => 'swiss');
    $out->write_seq($seq[$current]);
}

sub saveas_dialog
{
    # Create a new file selection widget
    my $saveas_dialog = new Gtk::FileSelection( "Save As..." );
    # Connect the ok_button to saveas_ok_sel function
    $saveas_dialog->ok_button->signal_connect( "clicked",
					       \&ok_saveas_dialog,
					       $saveas_dialog );
    # Connect the cancel_button to destroy the widget
    $saveas_dialog->cancel_button->signal_connect( "clicked",
						   sub { $saveas_dialog->destroy(); } );
    $saveas_dialog->show();
}

# Get the selected filename and print it to the console
sub ok_saveas_dialog
  {
    my ( $widget, $file_selection ) = @_;
    my $filename = $file_selection->get_filename();
    $widget->parent->parent->parent->destroy();
    $filename[$current] = $filename;
    my $out = Bio::SeqIO->new(-file => ">$filename[$current]" , '-format' => 'swiss');
    $out->write_seq($seq[$current]);
  }

sub init_comment_window
{
    $current_comment = 0;
    
    $comment_window = new Gtk::Dialog();
    $comment_window->set_default_size(650,300);
    $comment_window->set_policy($false,$true,$false);
    $comment_window->set_title("Comments");
    $comment_window->border_width(5);
    
    # frame
    $comment_frame = new Gtk::Frame( "Comment[".$current_comment."]" );
    
    # text widget
    $comment_edit = new Gtk::Text( undef, undef );
    $comment_edit->set_editable( $true );
    $comment_edit->set_word_wrap( $true );
	
    # vertical scrollbar for text widget
    my $scrollbar;
    $scrollbar = new Gtk::VScrollbar( $comment_edit->vadj );
	
    # horizontal box containing text widget and scrollbar
    my $hbox;
    $hbox = new Gtk::HBox( $false, 1 );
    $hbox->border_width( 1 );
    $hbox->pack_start( $comment_edit, $true, $true, 0);
    $hbox->pack_end( $scrollbar, $false, $true, 0);
    $comment_frame->add($hbox);
    $comment_window->vbox->pack_start( $comment_frame, $true, $true, 5);

    my $bbox = new Gtk::HBox( $false, 5 );
    $bbox->border_width(10);
    my $arrow = new Gtk::Arrow('right','out');
    my $button = new Gtk::Button();
    $button->add($arrow);
    $bbox->pack_end( $button, $false, $false, 0);
    $button->signal_connect
	( "clicked", 
	  # next comment button handler
	  sub { return if !defined($seq[$current]);
		 &store_current_comment;
		$current_comment++ 
		    if ($current_comment <((scalar $seq[$current]->annotation->each_Comment)-1));
		&update_comment_window;
	    } );

    $arrow = new Gtk::Arrow('left','out');
    $button = new Gtk::Button();
    $button->add($arrow);
    $bbox->pack_end( $button, $false, $false, 0);
    $button->signal_connect( "clicked", 
           # prev comment button handler
	   sub { return if !defined($seq[$current]);
		 &store_current_comment;
		 $current_comment-- 
		     if ($current_comment > 0);
		 &update_comment_window;
	       } );

    $button = new Gtk::Button("Add");
    $bbox->pack_start( $button, $false, $false, 0);
    $button->signal_connect( "clicked",
           # add comment button handler 
           sub { return if !defined($seq[$current]);
		 &store_current_comment;
		 my $comment = new Bio::Annotation::Comment;
		 $comment->text("");
		 $seq[$current]->annotation->add_Comment( $comment );
		 $current_comment = $seq[$current]->annotation->each_Comment - 1;
		 &update_comment_window;
	       } );

     $button = new Gtk::Button("Delete");
     $bbox->pack_start( $button, $false, $false, 0);
     $button->signal_connect( "clicked", 
           # delete comment button handler
           sub { return if !defined($seq[$current]); 
		 $seq[$current]->annotation->remove_Comment( $current_comment );
		 $current_comment = $current_comment - 1 
		     if ($current_comment > 0);
		 &update_comment_window;		 
	       } );

     $comment_window->vbox->pack_end( $bbox, $false, $false, 0);

     $bbox = new Gtk::HButtonBox();
     $bbox->set_layout("end");
	
     $button = new Gtk::Button( "Close" );
     $bbox->add( $button );
     $button->signal_connect("clicked",
           # close button handler
           sub{ $comment_window->hide();
		&store_current_comment;
	      });

     $comment_window->action_area->pack_start( $bbox, $true, $true, 0 );
     $comment_window->signal_connect_after( "delete_event",
           # window delete handler
           sub{ $comment_window->hide();
		&store_current_comment;
                return &Gtk::true;
	      });
}

sub store_current_comment
{
    (($seq[$current]->annotation->each_Comment)[$current_comment])->
	text($comment_edit->get_chars(0,-1) )
	    if ((defined($seq[$current])) && ($seq[$current]->annotation->each_Comment));
}

sub update_comment_window
{
    my ($show_me) = @_;
    $comment_frame->set_label("Comment[".$current_comment."]");
    # insert comment text
    $comment_edit->freeze();
    $comment_edit->delete_text(0,-1);
    if (defined($seq[$current]))
    {
	my @comment = $seq[$current]->annotation->each_Comment;
	$comment_edit->insert(undef,undef,undef, $comment[$current_comment]->text)
	    if (@comment);
    }
    $comment_edit->thaw();
    
    $comment_window->show_all() if (defined($show_me));
}

sub init_description_window
{
    $description_window = new Gtk::Dialog();
    $description_window->set_default_size(620,250);
    $description_window->border_width(5);
    $description_window->set_title("Description");
    
    # frame
    my $description_frame = new Gtk::Frame( "Description" );
    
    # text widget
    $description_edit = new Gtk::Text( undef, undef );
    $description_edit->set_editable( $true );
    $description_edit->set_word_wrap( $true );	
    
    # vertical scrollbar for text widget
    my $scrollbar;
    $scrollbar = new Gtk::VScrollbar( $description_edit->vadj );
    
    # horizontal box containing text widget and scrollbar
    my $hbox;
    $hbox = new Gtk::HBox( $false, 1 );
    $hbox->border_width( 1 );
    $hbox->pack_start( $description_edit, $true, $true, 0);
    $hbox->pack_end( $scrollbar, $false, $true, 0);
    $description_frame->add($hbox);
    $description_window->vbox->pack_start( $description_frame, $true, $true, 5);
    
    my $bbox = new Gtk::HButtonBox();
    $bbox->set_layout("end");
    
    my $button = new Gtk::Button( "Close" );
    $bbox->add( $button );
    $button->signal_connect("clicked",
           # close button handler
           sub{ $description_window->hide();
		$seq[$current]->desc($description_edit->get_chars(0,-1))
		    if $description_edit->get_chars(0,-1);
	      });

    $description_window->action_area->pack_start( $bbox, $true, $true, 0 );
    $description_window->signal_connect_after( "delete_event",
           # window delete handler
           sub{ $description_window->hide();
		$seq[$current]->desc($description_edit->get_chars(0,-1))
		    if $description_edit->get_chars(0,-1);
                return &Gtk::true;
	      });
}

sub update_description_window
{
    my ($show_me) = @_;
    $description_edit->freeze();
    $description_edit->delete_text(0,-1);
    $description_edit->insert(undef,undef,undef,$seq[$current]->desc)
	if defined($seq[$current]) && defined($seq[$current]->desc);
    $description_edit->thaw();
    
    $description_window->show_all() if (defined($show_me));
}

sub init_seqstats_window
{
    $seqstats_window = new Gtk::Dialog();
    $seqstats_window->border_width(5);
    $seqstats_window->set_default_size(100,250);
    $seqstats_window->set_title("Sequence Statistics");

    # frame
    my $seqstats_frame = new Gtk::Frame( "Sequence Statistics" );
    
    # text widget
    $seqstats_edit = new Gtk::Text( undef, undef );
    $seqstats_edit->set_editable( $false );
    $seqstats_edit->set_word_wrap( $true );
    
    # vertical scrollbar for text widget
    my $scrollbar;
    $scrollbar = new Gtk::VScrollbar( $seqstats_edit->vadj );
    
    # horizontal box containing text widget and scrollbar
    my $hbox;
    $hbox = new Gtk::HBox( $false, 1 );
    $hbox->border_width( 1 );
    $hbox->pack_start( $seqstats_edit, $true, $true, 0);
    $hbox->pack_end( $scrollbar, $false, $true, 0);
    $seqstats_frame->add($hbox);
    $seqstats_window->vbox->pack_start( $seqstats_frame, $true, $true, 5);
    
    my $bbox = new Gtk::HButtonBox();
    $bbox->set_layout("end");
    
    my $button = new Gtk::Button( "Close" );
    $bbox->add( $button );
    $button->signal_connect("clicked",
       # close button handler
       sub{ $seqstats_window->hide();
	  });
    
    $seqstats_window->action_area->pack_start( $bbox, $true, $true, 0 );
    $seqstats_window->signal_connect_after( "delete_event",
       # window delete handler
       sub{ $seqstats_window->hide();
	    return &Gtk::true;
	  });
}

sub update_seqstats_window
{
    my ($show_me) = @_;
    my ($data,$weight,$count_hash,$percent);

    $seqstats_edit->freeze();
    $seqstats_edit->delete_text(0,-1);
    if (defined($seq[$current]))
    {
	$data = $seq[$current]->id."\n\n";
	$weight = Bio::Tools::SeqStats->get_mol_wt($seq[$current]->primary_seq);
	if ($$weight[0] == $$weight[1]) {
	    $data .= "Molecular weight of sequence equals to ".$$weight[0]."\n\n";
	} else {
	    $data .= "Molecular weight of sequence is greater than ";
	    $data .= $$weight[0]." and less than ".$$weight[1]."\n\n";
	}
	$count_hash = Bio::Tools::SeqStats->count_monomers($seq[$current]->primary_seq);
	$data .= "Amino Acids:\n";
	foreach (sort keys %$count_hash)
	{
	    $percent = sprintf "%.1f",
	    (($$count_hash{$_} / $seq[$current]->length)*100);
	    $data .= "${_}: ".$$count_hash{$_}." (${percent}%) \n"
	    }
	$seqstats_edit->insert(undef,undef,undef,$data)
	}
    $seqstats_edit->thaw();
    
    $seqstats_window->show_all() if (defined($show_me));
}

sub init_dblink_window
{
    $current_dblink = 0;
    
    $dblink_window = new Gtk::Dialog();
    $dblink_window->set_default_size(500,400);
    $dblink_window->set_policy($true,$true,$false);
    $dblink_window->set_title("Database Links");
    $dblink_window->border_width(5);
    
    # Create a scrolled window to pack the CList widget into
    my $scrolled_window = new Gtk::ScrolledWindow( undef, undef );
    $dblink_window->vbox->pack_start( $scrolled_window, $true, $true, 0 );
    $scrolled_window->set_policy( 'automatic', 'always' );

    # Create the CList. For this example we use 2 columns
    $dblink_clist = new_with_titles Gtk::CList( "Primary Id","Database" );

    # When a selection is made, we want to know about it. The callback
    # used is selection_made, and its code can be found further down
    $dblink_handler_id = $dblink_clist->signal_connect( "select_row", 
       sub{ return if (!defined($seq[$current]));
	    my ( $clist, $row ) = @_;
	    &store_current_dblink;
	    $current_dblink = $row;
	    &update_dblink_window;
          } );

    # It isn't necessary to shadow the border, but it looks nice :)
    $dblink_clist->set_shadow_type( 'out' );

    # What however is important, is that we set the column widths as
    # they will never be right otherwise. Note that the columns are
    # numbered from 0 and up (to 1 in this case).
    $dblink_clist->set_column_width( 0, 150 );

    # Add the CList widget to the vertical box
    $scrolled_window->add( $dblink_clist );

    my $bbox = new Gtk::HBox( $false, 5 );
    $bbox->border_width(10);
    my $arrow = new Gtk::Arrow('down','out');
    my $button = new Gtk::Button();
    $button->add($arrow);
    $bbox->pack_end( $button, $false, $false, 0);
    $button->signal_connect
	( "clicked", 
	  # next dblink button handler
	  sub { return if (!defined($seq[$current]));
		&store_current_dblink;
		$current_dblink++
		    if ($current_dblink <((scalar $seq[$current]->annotation->each_DBLink)-1));
		&update_dblink_window;
	      } );

    $arrow = new Gtk::Arrow('up','out');
    $button = new Gtk::Button();
    $button->add($arrow);
    $bbox->pack_end( $button, $false, $false, 0);
    $button->signal_connect( "clicked", 
           # prev comment button handler
	   sub { return if (!defined($seq[$current]));
		 &store_current_dblink;
		 $current_dblink--
		     if ($current_dblink > 0);
		 &update_dblink_window;
	       } );

    $button = new Gtk::Button("Add");
    $bbox->pack_start( $button, $false, $false, 0);
    $button->signal_connect( "clicked",
           # add comment button handler 
           sub { return if (!defined($seq[$current]));
		 &store_current_dblink;
		 my $dblink = new Bio::Annotation::DBLink;
		 $dblink->primary_id("<New>");
		 $seq[$current]->annotation->add_DBLink( $dblink );
		 $current_dblink = $seq[$current]->annotation->each_DBLink - 1;
		 $dblink_clist->append("","");
		 &update_dblink_window;
	       } );

     $button = new Gtk::Button("Delete");
     $bbox->pack_start( $button, $false, $false, 0);
     $button->signal_connect( "clicked", 
           # delete comment button handler
           sub { return if !defined($seq[$current]); 
		 $seq[$current]->annotation->remove_DBLink( $current_dblink );
		 $dblink_clist->remove($current_dblink);
		 $current_dblink-- if ($current_dblink > 0);
		 &update_dblink_window;
	       } );

     $dblink_window->vbox->pack_start( $bbox, $false, $false, 0);

    # horizontal box containing primary_id & optional_id entries
    my $hbox;
    $hbox = new Gtk::HBox( $true, 10 );
    $hbox->border_width( 1 );

    # text entries
    $dblink_entry[0] = new Gtk::Entry();
    my $frame = new Gtk::Frame("primary id");
    $frame->add($dblink_entry[0]);
    $hbox->pack_start( $frame, $true, $true, 0);

    $dblink_entry[1] = new Gtk::Entry();
    $frame = new Gtk::Frame("optional id");
    $frame->add($dblink_entry[1]);
    $hbox->pack_end( $frame, $true, $true, 0);

    $dblink_window->vbox->pack_start( $hbox, $false, $false, 5);

    $dblink_entry[2] = new Gtk::Entry();
    $frame = new Gtk::Frame("Database");
    $frame->add($dblink_entry[2]);
    $dblink_window->vbox->pack_start( $frame, $false, $false, 5);

    $dblink_entry[3] = new Gtk::Entry();
    $frame = new Gtk::Frame("Comment");
    $frame->add($dblink_entry[3]);
    $dblink_window->vbox->pack_end( $frame, $false, $false, 5);

     $bbox = new Gtk::HButtonBox();
     $bbox->set_layout("end");
	
     $button = new Gtk::Button( "Close" );
     $bbox->add( $button );
     $button->signal_connect("clicked",
           # close button handler
           sub{ $dblink_window->hide();
		&store_current_dblink;
	      });

     $dblink_window->action_area->pack_start( $bbox, $true, $true, 0 );
     $dblink_window->signal_connect_after( "delete_event",
           # window delete handler
           sub{ $dblink_window->hide();
		&store_current_dblink;
                return &Gtk::true;
	      });
}

sub store_current_dblink
{
    if ((defined($seq[$current])) && ($seq[$current]->annotation->each_DBLink))
    {
	(($seq[$current]->annotation->each_DBLink)[$current_dblink])->
	    primary_id($dblink_entry[0]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_DBLink)[$current_dblink])->
	    optional_id($dblink_entry[1]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_DBLink)[$current_dblink])->
	    database($dblink_entry[2]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_DBLink)[$current_dblink])->
	    comment($dblink_entry[3]->get_chars(0,-1) );		
    }
}

sub update_dblink_window
{
    my ($show_me) = @_;
    $dblink_window->show_all() if (defined($show_me));

    $dblink_clist->freeze();
    if (!defined($seq[$current]))
    {
	$dblink_clist->clear();
	$dblink_clist->thaw();
	foreach (@dblink_entry) { $_->set_text(""); }
	return;
    }
    my @dblinks = $seq[$current]->annotation->each_DBLink;
    # reset clist if rows are different to links
    if ($dblink_clist->rows != @dblinks) {
	$dblink_clist->clear();
	foreach (@dblinks) { $dblink_clist->append("",""); }
    }
    # redraw references
    for(my $i=0;$i<@dblinks;$i++)
    {
	$dblink_clist->set_text($i,0,$dblinks[$i]->primary_id);
	$dblink_clist->set_text($i,1,$dblinks[$i]->database);
    }
    # redraw text widgets
    foreach (@dblink_entry) { $_->set_text(""); }
    if (@dblinks)
    {
	$dblink_entry[0]->set_text($dblinks[$current_dblink]->primary_id);
	$dblink_entry[1]->set_text($dblinks[$current_dblink]->optional_id);
	$dblink_entry[2]->set_text($dblinks[$current_dblink]->database);
	$dblink_entry[3]->set_text($dblinks[$current_dblink]->comment);
    }

    $dblink_clist->moveto($current_dblink,0,0.3,0.0)
	if ($dblink_clist->row_is_visible($current_dblink) ne 'full');
    $dblink_clist->signal_handler_block($dblink_handler_id);
    $dblink_clist->select_row($current_dblink,0);
    $dblink_clist->signal_handler_unblock($dblink_handler_id);
    Gtk::CList::set_focus_row($dblink_clist,$current_dblink);
    $dblink_clist->thaw();
}

sub init_reference_window
{
    $current_ref = 0;
    
    $ref_window = new Gtk::Dialog();
    $ref_window->set_default_size(620,500);
    $ref_window->set_policy($true,$true,$false);
    $ref_window->set_title("References");
    $ref_window->border_width(5);
    
    # Create a scrolled window to pack the CList widget into
    my $scrolled_window = new Gtk::ScrolledWindow( undef, undef );
    $ref_window->vbox->pack_start( $scrolled_window, $true, $true, 0 );
    $scrolled_window->set_policy( 'automatic', 'always' );

    # Create the CList. For this example we use 2 columns
    $ref_clist = new_with_titles Gtk::CList( "Medline","Title","Authors" );

    # When a selection is made, we want to know about it. The callback
    # used is selection_made, and its code can be found further down
    $ref_handler_id = $ref_clist->signal_connect( "select_row", 
       sub{ return if (!defined($seq[$current]));
	    my ( $clist, $row ) = @_;
	    &store_current_reference;
	    $current_ref = $row;
	    &update_reference_window;
          } );

    # It isn't necessary to shadow the border, but it looks nice :)
    $ref_clist->set_shadow_type( 'out' );

    # What however is important, is that we set the column widths as
    # they will never be right otherwise. Note that the columns are
    # numbered from 0 and up (to 1 in this case).
    $ref_clist->set_column_width( 0, 70 );
    $ref_clist->set_column_width( 1, 350 );
    $ref_clist->set_column_width( 2, 300 );

    # Add the CList widget to the vertical box
    $scrolled_window->add( $ref_clist );

    my $bbox = new Gtk::HBox( $false, 5 );
    $bbox->border_width(10);
    my $arrow = new Gtk::Arrow('down','out');
    my $button = new Gtk::Button();
    $button->add($arrow);
    $bbox->pack_end( $button, $false, $false, 0);
    $button->signal_connect
	( "clicked", 
	  # next ref button handler
	  sub { return if (!defined($seq[$current]));
		&store_current_reference;
		$current_ref++
		    if ($current_ref <((scalar $seq[$current]->annotation->each_Reference)-1));
		&update_reference_window;
	    } );

    $arrow = new Gtk::Arrow('up','out');
    $button = new Gtk::Button();
    $button->add($arrow);
    $bbox->pack_end( $button, $false, $false, 0);
    $button->signal_connect( "clicked", 
           # prev comment button handler
	   sub { return if (!defined($seq[$current]));
		 &store_current_reference;
		 $current_ref--
		     if ($current_ref > 0);
		 &update_reference_window;
	       } );

    $button = new Gtk::Button("Add");
    $bbox->pack_start( $button, $false, $false, 0);
    $button->signal_connect( "clicked",
           # add comment button handler 
           sub { return if (!defined($seq[$current]));
		 &store_current_reference;
		 my $ref = new Bio::Annotation::Reference;
		 $ref->medline("<New>");
		 $seq[$current]->annotation->add_Reference( $ref );
		 $ref_clist->append("","","");
		 $current_ref = ($seq[$current]->annotation->each_Reference)-1;
		 &update_reference_window;
	       } );

     $button = new Gtk::Button("Delete");
     $bbox->pack_start( $button, $false, $false, 0);
     $button->signal_connect( "clicked", 
           # delete comment button handler
           sub { return if !defined($seq[$current]); 
		 $seq[$current]->annotation->remove_Reference( $current_ref );
		 $ref_clist->remove($current_ref);
		 $current_ref-- if ($current_ref > 0);
		 &update_reference_window;
	       } );

     $ref_window->vbox->pack_start( $bbox, $false, $false, 0);

    # horizontal box containing primary_id & optional_id entries
    my $hbox;
    $hbox = new Gtk::HBox( $true, 10 );
    $hbox->border_width( 1 );

    # text entries
    $ref_entry[0] = new Gtk::Entry();
    my $frame = new Gtk::Frame("Title");
    $frame->add($ref_entry[0]);
    $ref_window->vbox->pack_start( $frame, $false, $false, 5);

    $ref_entry[1] = new Gtk::Entry();
    $frame = new Gtk::Frame("Authors");
    $frame->add($ref_entry[1]);
    $ref_window->vbox->pack_start( $frame, $false, $false, 5);

    # horizontal box
    $hbox = new Gtk::HBox( $true, 10 );
    $hbox->border_width( 1 );

    # text entries
    $ref_entry[2] = new Gtk::Entry();
    $frame = new Gtk::Frame("Comment");
    $frame->add($ref_entry[2]);
    $hbox->pack_start( $frame, $true, $true, 0);

    $ref_entry[3] = new Gtk::Entry();
    $frame = new Gtk::Frame("Location");
    $frame->add($ref_entry[3]);
    $hbox->pack_end( $frame, $true, $true, 0);

    $ref_window->vbox->pack_start( $hbox, $false, $false, 5);

    # horizontal box
    $hbox = new Gtk::HBox( $false, 10 );
    $hbox->border_width( 1 );

    # text entries
    $ref_entry[4] = new Gtk::Entry();
    $frame = new Gtk::Frame("Medline");
    $frame->add($ref_entry[4]);
    $hbox->pack_start( $frame, $false, $false, 0);

#    $ref_entry[5] = new Gtk::Entry();
#    $frame = new Gtk::Frame("Start");
#    $frame->add($ref_entry[5]);
#    $hbox->pack_start( $frame, $false, $false, 0);

    $ref_entry[5] = new Gtk::Entry();
    $frame = new Gtk::Frame("Reference Position");
    $frame->add($ref_entry[5]);
    $hbox->pack_end( $frame, $true, $true, 0);

    $ref_window->vbox->pack_start( $hbox, $false, $false, 5);


     $bbox = new Gtk::HButtonBox();
     $bbox->set_layout("end");
	
     $button = new Gtk::Button( "Close" );
     $bbox->add( $button );
     $button->signal_connect("clicked",
           # close button handler
           sub{ $ref_window->hide();
		&store_current_reference;
	      });

     $ref_window->action_area->pack_start( $bbox, $true, $true, 0 );
     $ref_window->signal_connect_after( "delete_event",
           # window delete handler
           sub{ $ref_window->hide();
		&store_current_reference;
                return &Gtk::true;
	      });
}

sub store_current_reference
{
    if ((defined($seq[$current])) && ($seq[$current]->annotation->each_Reference))
    {
	(($seq[$current]->annotation->each_Reference)[$current_ref])->
	    title($ref_entry[0]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_Reference)[$current_ref])->
	    authors($ref_entry[1]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_Reference)[$current_ref])->
	    comment($ref_entry[2]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_Reference)[$current_ref])->
	    location($ref_entry[3]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_Reference)[$current_ref])->
	    medline($ref_entry[4]->get_chars(0,-1) );		
#	(($seq[$current]->annotation->each_Reference)[$current_ref])->
#	    start($ref_entry[5]->get_chars(0,-1) );		
	(($seq[$current]->annotation->each_Reference)[$current_ref])->
	    rp($ref_entry[5]->get_chars(0,-1) );		
    }
}

sub update_reference_window
{
    my ($show_me) = @_;
    $ref_window->show_all() if (defined($show_me));

    $ref_clist->freeze();
    if (!defined($seq[$current]))
    {
	$ref_clist->clear();
	$ref_clist->thaw();
	foreach (@ref_entry) { $_->set_text(""); }
	return;
    }
    my @refs = $seq[$current]->annotation->each_Reference;
    # reset clist if rows are different to references
    if ($ref_clist->rows != @refs) {
	$ref_clist->clear();
	foreach (@refs) { $ref_clist->append("","",""); }
    }
    # redraw references
    for(my $i=0;$i<@refs;$i++)
    {
	$ref_clist->set_text($i,0,$refs[$i]->medline)
	  if ($refs[$i]->medline);
	$ref_clist->set_text($i,1,$refs[$i]->title)
	  if ($refs[$i]->title);
	$ref_clist->set_text($i,2,$refs[$i]->authors)
	  if ($refs[$i]->authors);
    }
    # redraw text widgets
    foreach (@ref_entry) { $_->set_text(""); }
    if (@refs) {
	$ref_entry[0]->set_text($refs[$current_ref]->title);
	$ref_entry[1]->set_text($refs[$current_ref]->authors);
	$ref_entry[2]->set_text($refs[$current_ref]->comment);
	$ref_entry[3]->set_text($refs[$current_ref]->location);
	$ref_entry[4]->set_text($refs[$current_ref]->medline);
#	$ref_entry[5]->set_text($refs[$current_ref]->start);
	$ref_entry[5]->set_text($refs[$current_ref]->rp);
    }

    $ref_clist->moveto($current_ref,0,0.3,0.0)
	if ($ref_clist->row_is_visible($current_ref) ne 'full');
    $ref_clist->signal_handler_block($ref_handler_id);
    $ref_clist->select_row($current_ref,0);
    $ref_clist->signal_handler_unblock($ref_handler_id);
    Gtk::CList::set_focus_row($ref_clist,$current_ref);
    $ref_clist->thaw();
}


sub init_about_dialog {
 	my ($window,$bg,$tbox,$vbox,$hbox,$sep,$butbox,$button,$pixmap);
 	$about_dialog = new Gtk::Window("dialog");
 	$about_dialog->set_title("About gSequence");
 	$about_dialog->signal_connect_after("destroy" => 
					    sub { $about_dialog->hide; 
					          return &Gtk::true; });
 	$about_dialog->set_default_size('350','350');
 	$about_dialog->set_policy(1,1,0);
 	$window = $about_dialog->window;
 	$bg = $about_dialog->style->bg('normal');
 	$vbox= new Gtk::VBox(0,0);
 	$about_dialog->add($vbox);
    	$tbox = new Gtk::Label("\ngSequence\nAuthor: Lorenz Pollak\n\n
gSequence is cool! :-)\n(this text is to be written...)
\n");
 	$vbox->pack_start($tbox,1,1,1);
   
    	$hbox = new Gtk::HBox(0,0);   
    	$vbox->pack_start($hbox,0,0,0);
 	$sep = new Gtk::HSeparator;
   	$sep->set_usize(-1,5);
    	$vbox->pack_start($sep,0,1,0);
    
     	$butbox = new Gtk::HButtonBox;
     	$butbox->set_usize(-1,32);
     	$vbox->pack_start($butbox, 0,1,0); 
 	$button = new_with_label Gtk::Button("OK");
     	$button->set_usize(50,-1);
     	$button->signal_connect('clicked', sub { $about_dialog->hide; });
	$button->can_default(1);
	$button->grab_default;
	$butbox->add($button);
     
  return 1;
}    

sub init_feature_window
{
    $current_feature_item = 0;
    
    $feature_window = new Gtk::Dialog();
    $feature_window->set_default_size(500,400);
    $feature_window->set_policy($true,$true,$false);
    $feature_window->set_title("Sequence Features");
    $feature_window->border_width(5);

    my $pane = new Gtk::HPaned();
    $feature_window->vbox->pack_start( $pane, $true, $true, 0);
    $pane->set_handle_size( 10 );
    $pane->set_gutter_size( 8 );

    # Create a VBox for the Entry and Tree Scrolled Window
    my $vbox = new Gtk::VBox( $false, 0 );
    $pane->add1( $vbox );

    # Create a ScrolledWindow for the tree
    my $tree_scrolled_win = new Gtk::ScrolledWindow( undef, undef );
    $tree_scrolled_win->set_usize( 150, 400 );
    $vbox->pack_start( $tree_scrolled_win, $true, $true, 0 );
    $tree_scrolled_win->set_policy( 'automatic', 'automatic' );

    #my $list_scrolled_win = new Gtk::ScrolledWindow( undef, undef );
    #$list_scrolled_win->set_policy( 'automatic', 'automatic' );
    $vbox = new Gtk::VBox( $false, 0 );
    $pane->add2( $vbox );

    # add stuff to the vbox
    # text entries
    my $hbox = new Gtk::HBox( $true, 10 );

    $feature_entry[0] = new Gtk::Entry();
    my $frame = new Gtk::Frame("Primary Tag");
    $frame->add($feature_entry[0]);
    $hbox->pack_start( $frame, $true, $true, 0);

    $feature_entry[1] = new Gtk::Entry();
    $frame = new Gtk::Frame("Source Tag");
    $frame->add($feature_entry[1]);
    $hbox->pack_end( $frame, $true, $true, 0);

    $vbox->pack_start( $hbox, $false, $false, 5);

    $hbox = new Gtk::HBox( $true, 10 );

    my $adj = new Gtk::Adjustment( 0, 0, 0, 0, 0, 0 );
    $feature_spinner[0] = new Gtk::SpinButton( $adj, 0.0, 0 );
    $feature_spinner[0]->signal_connect( "changed", \&select_feature_region);
    $frame = new Gtk::Frame("Start");
    $frame->add($feature_spinner[0]);
    $hbox->pack_start( $frame, $true, $true, 0);

    $adj = new Gtk::Adjustment( 0, 0, 0, 0, 0, 0 );
    $feature_spinner[1] = new Gtk::SpinButton( $adj, 0.0, 0 );
    $feature_spinner[1]->signal_connect( "changed", \&select_feature_region);
    $frame = new Gtk::Frame("End");
    $frame->add($feature_spinner[1]);
    $hbox->pack_start( $frame, $true, $true, 0);

    $frame = new Gtk::Frame("Strand");
    $hbox->pack_start( $frame, $true, $true, 0);
    $frame = new Gtk::Frame("Score");
    $hbox->pack_start( $frame, $true, $true, 0);

    $vbox->pack_start( $hbox, $false, $false, 5);

    $feature_entry[2] = new Gtk::Entry();
    $frame = new Gtk::Frame("Description");
    $frame->add($feature_entry[2]);

    $vbox->pack_start( $frame, $false, $false, 5);

    my $bbox = new Gtk::HBox( $false, 5 );
    $bbox->border_width(10);
    my $button = new Gtk::Button("Add");
    $bbox->pack_start( $button, $false, $false, 0);
    $button->signal_connect( "clicked",
           # add comment button handler 
           sub { return if (!defined($seq[$current]));
		 &store_current_feature if ($current_feature_item);
		 my $feature = new Bio::SeqFeature::Generic;
		 $feature->primary_tag("<New>");
		 $seq[$current]->add_SeqFeature( $feature );
		 my $item_new = new_with_label Gtk::TreeItem( "<New>" );
		 $item_new->set_user_data( $feature );
		 $item_new->signal_connect( 'select', \&select_feature_item );
		 $current_feature_item->parent->append( $item_new )
		     if ($current_feature_item);
		 $feature_tree->append( $item_new ) if (!$current_feature_item);
		 $item_new->show();
		 $current_feature_item->deselect()
		     if ($current_feature_item);
		 $item_new->select();
	       } );
    $button = new Gtk::Button("Add Subfeature");
    $bbox->pack_start( $button, $false, $false, 0);
    $button->signal_connect( "clicked",
           # add comment button handler 
           sub { return if (!defined($seq[$current])||!$current_feature_item);
		 &store_current_feature;
		 my $feature = new Bio::SeqFeature::Generic;
		 $feature->primary_tag("<New>");
		 $feature->start($current_feature_item->get_user_data->start);
		 $feature->end($current_feature_item->get_user_data->end);
		 $current_feature_item->get_user_data->add_sub_SeqFeature( $feature );
		 my $new_subtree = new Gtk::Tree();
		 $current_feature_item->set_subtree( $new_subtree );
		 my $item_new = new_with_label Gtk::TreeItem( "<New>" );
		 $item_new->set_user_data( $feature );
		 $item_new->signal_connect( 'select', \&select_feature_item );
		 $new_subtree->append( $item_new );
		 $item_new->show();
		 $current_feature_item->deselect();
		 $current_feature_item->expand();
		 $item_new->select();
	       } );
     $button = new Gtk::Button("Delete");
     $bbox->pack_start( $button, $false, $false, 0);
     $button->signal_connect( "clicked", 
           # delete comment button handler
           sub { return if (!$current_feature_item); 
		 &store_current_feature;
		 my $flist = $seq[$current]->{_as_feat};
		 my $pos;
		 for(my $i=0;$i<@$flist;$i++) {
		   $pos=$i if $$flist[$i]==$current_feature_item->get_user_data();
		 }
		 splice @$flist, $pos, 1;
		 $seq[$current]->{_as_feat} = $flist;
		 $current_feature_item->parent->remove_item($current_feature_item);
		 $current_feature_item=0;
	       } );

     $vbox->pack_end( $bbox, $false, $false, 0);

    # Create root tree
    $feature_tree = new Gtk::Tree();
    $tree_scrolled_win->add_with_viewport( $feature_tree );
    $feature_tree->set_selection_mode( 'single' );
    $feature_tree->set_view_mode( 'item' );

    $bbox = new Gtk::HButtonBox();
    $bbox->set_layout("end");
    
    $button = new Gtk::Button( "Close" );
    $bbox->add( $button );
    $button->signal_connect("clicked",
			    # close button handler
			    sub{ $feature_window->hide();
				 &store_current_feature;
			     });
    
    $feature_window->action_area->pack_start( $bbox, $true, $true, 0 );
    $feature_window->signal_connect_after( "delete_event",
					   # window delete handler
					   sub{ $feature_window->hide();
						&store_current_feature;
						return &Gtk::true;
					    });
}

# Callback for expanding tree
sub expand_feature_tree
  {
    my ( $item, $subtree ) = @_;
    my ($feature,$subfeature,$item_new,$new_subtree);
    $feature = $item->get_user_data();

    foreach $subfeature ($feature->sub_SeqFeature)
      {
	  $item_new = new_with_label Gtk::TreeItem( $subfeature->primary_tag );
	  $item_new->set_user_data( $subfeature );
	  $item_new->signal_connect( 'select', \&select_feature_item );
	  $subtree->append( $item_new );
	  $item_new->show();
	  
	  if ( $subfeature->sub_SeqFeature )
	  {
	      $new_subtree = new Gtk::Tree();
	      $item_new->set_subtree( $new_subtree );
	      $item_new->signal_connect( 'expand',
					 \&expand_feature_tree,
					 $new_subtree );
	      $item_new->signal_connect( 'collapse', \&collapse_feature_tree );
	  }
	  $item_new->expand();
      }
  }


# Callback for collapsing tree
sub collapse_feature_tree
  {
    my ( $item ) = @_;

    my $subtree = new Gtk::Tree();

    $item->remove_subtree();
    $item->set_subtree( $subtree );
    $item->signal_connect( 'expand', \&expand_feature_tree, $subtree );
  }


sub store_current_feature
{
  if ((defined($seq[$current])) && ($seq[$current]->top_SeqFeatures) && ($current_feature_item))
  {
    my $current_feature = $current_feature_item->get_user_data();
    $current_feature->primary_tag( $feature_entry[0]->get_chars(0,-1) );		
    $current_feature->source_tag( $feature_entry[1]->get_chars(0,-1) );		
    if ($current_feature->has_tag("description"))
    {
      $current_feature->remove_tag("description");
      $current_feature->add_tag_value("description",
				      $feature_entry[2]->get_chars(0,-1));
    }
    $current_feature->start($feature_spinner[0]->get_value_as_int());
    $current_feature->end($feature_spinner[1]->get_value_as_int());
    # set tree item
    ($current_feature_item->children)[0]->set($current_feature->primary_tag);
  }
}

sub select_feature_item
{
    my ($widget) = @_;
    &store_current_feature;
    $current_feature_item->deselect()
      if $current_feature_item;
    $current_feature_item = $widget;
    &update_feature_paned2;
}

sub update_feature_paned2
{
  $feature_entry[0]->set_text("");
  $feature_entry[1]->set_text("");
  $feature_entry[2]->set_text("");

  return if (!defined($seq[$current])||(!$current_feature_item));
  my $current_feature = $current_feature_item->get_user_data();
  $feature_entry[0]->set_text($current_feature->primary_tag);
  $feature_entry[1]->set_text($current_feature->source_tag)
    if (defined($current_feature->source_tag));
  $feature_entry[2]->set_text(($current_feature->each_tag_value("description"))[0])
    if ($current_feature->has_tag("description"));
  my $adj = new Gtk::Adjustment($current_feature->start,
				0,
				$seq[$current]->length-1,
				1,
				1,
				0
			       );
  $feature_spinner[0]->set_adjustment($adj);
  $feature_spinner[0]->set_value($current_feature->start);
  $feature_spinner[0]->show_all();
  $adj = new Gtk::Adjustment($current_feature->end,
			     0,
			     $seq[$current]->length-1,
			     1,
			     1,
			     0
			    );
  $feature_spinner[1]->set_adjustment($adj);
  $feature_spinner[1]->set_value($current_feature->end);
  $feature_spinner[1]->show_all();
}

sub select_feature_region
{
  $seq_edit[$current]->freeze;
  $seq_edit[$current]->select_region($feature_spinner[0]->get_value_as_int(),
			   $feature_spinner[1]->get_value_as_int()+1);
  $seq_edit[$current]->thaw;
}

sub update_feature_window
{
    my ($show_me) = @_;
    $feature_window->show_all() if (defined($show_me));

    $feature_tree->clear_items(0,-1);
    if (!defined($seq[$current]))
    {
	&update_feature_paned2;
	return;
    }

    my ($item_new,$new_subtree);
    foreach ($seq[$current]->top_SeqFeatures)
      {
	  $item_new = new_with_label Gtk::TreeItem( $_->primary_tag );
	  $item_new->set_user_data( $_ );
	  $item_new->signal_connect( 'select', \&select_feature_item );
	  $feature_tree->append( $item_new );
	  if ( $_->sub_SeqFeature )
	  {
	      $new_subtree = new Gtk::Tree();
	      $item_new->set_subtree( $new_subtree );
	      $item_new->signal_connect( 'expand',
					 \&expand_feature_tree,
					 $new_subtree );
	      $item_new->signal_connect( 'collapse', \&collapse_feature_tree );
	  }
	  $item_new->expand();
      }
    $feature_tree->select_item($current_feature_item) 
      if $current_feature_item;
    $feature_tree->show_all();

    &update_feature_paned2;
}

sub store_prefs
{
}

sub update_pref_window
{
  $pref_window->show_all();
}

sub init_pref_window
{
  $pref_window = new Gtk::Dialog();
  $pref_window->set_default_size(500,400);
  $pref_window->set_policy($true,$true,$false);
  $pref_window->border_width( 5 );

  # Create a new notebook, place the position of the tabs
  my $notebook = new Gtk::Notebook();
  $pref_window->vbox->pack_start( $notebook, $true, $true, 0);
  $notebook->set_tab_pos( 'top' );

  my $main_vbox = new Gtk::VBox($false,10);

  my $label = new Gtk::Label( "Import Options" );
  my $frame = new Gtk::Frame("Flat File Indexes");
  my $vbox = new Gtk::VBox($false,10);
  $frame->add($vbox);
  $main_vbox->pack_start($frame,$false,$false,10);

  $notebook->append_page( $main_vbox, $label );

  my $hbox = new Gtk::HBox($false,0);

  $pref_entry[0] = new Gtk::Entry();
  $frame = new Gtk::Frame("Indexes Directory");
  $frame->add($pref_entry[0]);
  $hbox->pack_start( $frame, $true, $false, 0);

  $pref_entry[1] = new Gtk::Entry();
  $frame = new Gtk::Frame("Index Type");
  $frame->add($pref_entry[1]);
  $hbox->pack_start( $frame, $false, $false, 0);

  $vbox->pack_start( $hbox, $false, $false, 0);

  $pref_entry[2] = new Gtk::Entry();
  $frame = new Gtk::Frame("Fasta Index Name");
  $frame->add($pref_entry[2]);
  $vbox->pack_start( $frame, $false, $false, 0);

  $pref_entry[3] = new Gtk::Entry();
  $frame = new Gtk::Frame("SwissProt Index Name");
  $frame->add($pref_entry[3]);
  $vbox->pack_start( $frame, $false, $false, 0);

  $pref_entry[4] = new Gtk::Entry();
  $frame = new Gtk::Frame("SwissPfam Index Name");
  $frame->add($pref_entry[4]);
  $vbox->pack_start( $frame, $false, $false, 0);

  $frame = new Gtk::Frame("Remote DBs");
  $hbox = new Gtk::HBox($false,10);
  $frame->add($hbox);
  $main_vbox->pack_start($frame,$false,$false,10);

  $pref_entry[5] = new Gtk::Entry();
  $frame = new Gtk::Frame("AceDB host");
  $frame->add($pref_entry[5]);
  $hbox->pack_start( $frame, $true, $false, 0);

  $pref_entry[6] = new Gtk::Entry();
  $frame = new Gtk::Frame("AceDB port");
  $frame->add($pref_entry[6]);
  $hbox->pack_start( $frame, $false, $false, 0);

  $notebook->set_page( 0 );

  my $bbox = new Gtk::HButtonBox();
  $bbox->set_layout("end");

  my $button = new Gtk::Button( "Save" );
  $bbox->add( $button );
  $button->signal_connect("clicked",
			  # close button handler
			  sub{ $pref_window->hide();
			       &store_prefs();
			     });
  
  $button = new Gtk::Button( "Close" );
  $bbox->add( $button );
  $button->signal_connect("clicked",
			  # close button handler
			  sub{ $pref_window->hide();
			     });
  
  $pref_window->action_area->pack_start( $bbox, $true, $true, 0 );
  $pref_window->signal_connect_after( "delete_event",
					 # window delete handler
					 sub{ $pref_window->hide();
					      return &Gtk::true;
					    });
}