#!/usr/bin/perl use warnings; use strict; use Glib qw( TRUE FALSE ); use Gtk2 -init; use Gtk2::SimpleList; use File::Spec::Functions; sub pathname_from_iter { my ( $tree_model, $iter ) = @_; my @path; while( $iter ) { unshift @path, $tree_model->get( $iter, 0 ); $iter = $tree_model->iter_parent( $iter ); } return catfile '', @path; } sub process_dir_at_iter(&;@) { my ( $process, $tree_model, $iter ) = @_; my $pathname = pathname_from_iter( $tree_model, $iter ); if( opendir my $dh, $pathname ) { my @content; Glib::Idle->add( sub { my $fname = readdir $dh; if( defined $fname ) { push @content, $fname; } else { closedir $dh; $process->( $pathname, sort +( no_upwards @content ) ); } return defined $fname; } ); } } sub populate_node { my ( $tree_store, $node ) = @_; my $child; # remove any previously added children $tree_store->remove( $child ) while $child = $tree_store->iter_children( $node ); # then put the newly read ones back in process_dir_at_iter { my $path = shift; my @subdir = grep { -d catfile( $path, $_ ) and -r _ } @_; $tree_store->set( $tree_store->append( $node ), 0 => $_ ) for @subdir; } $tree_store, $node; } sub make_filelist { return Gtk2::SimpleList->new( Filename => 'text' ); } sub make_dirmodel { my $dirmodel = Gtk2::TreeStore->new( qw( Glib::String ) ); my $child = $dirmodel->append( undef ); $dirmodel->set( $child, 0 => '/' ); return $dirmodel; } sub make_dirtree { my ( $filelist, $dirmodel ) = @_; my $dirtree = Gtk2::TreeView->new( $dirmodel ); $dirtree->append_column( Gtk2::TreeViewColumn->new_with_attributes( '', Gtk2::CellRendererText->new(), text => 0 ) ); $dirtree->set_headers_visible( FALSE ); $dirtree->signal_connect( cursor_changed => sub { my ( $tree_view ) = @_; process_dir_at_iter { my $path = shift; @{ $filelist->{ data } } = grep ! -d catfile( $path, $_ ), @_; } $tree_view->get_selection->get_selected; return; } ); $dirtree->signal_connect( row_expanded => sub { my ( $tree_view, $iter, $tree_path ) = @_; my $tree_model = $tree_view->get_model(); my $child = $tree_model->iter_children( $iter ); while ( $child ) { populate_node( $tree_model, $child ); $child = $tree_model->iter_next( $child ); } } ); return $dirtree; } sub make_scrolledwindow { my ( $widget ) = @_; my $sw = Gtk2::ScrolledWindow->new; $sw->set_policy( qw( automatic automatic ) ); $sw->add_with_viewport( $widget ); return $sw; } sub make_hpaned { my ( $left, $right ) = @_; my $hpaned = Gtk2::HPaned->new; $hpaned->pack1( make_scrolledwindow( $left ), TRUE, TRUE ); $hpaned->pack2( make_scrolledwindow( $right ), TRUE, TRUE ); return $hpaned; } sub make_window { my $window = Gtk2::Window->new; $window->signal_connect( destroy => sub { Gtk2->main_quit } ); $window->set_default_size( 800, 600 ); $window->add( @_ ); return $window; } my $filelist = make_filelist(); my $dirmodel = make_dirmodel(); make_window( make_hpaned( make_dirtree( $filelist, $dirmodel ), $filelist, ), )->show_all; populate_node( $dirmodel, $dirmodel->get_iter_first ); Gtk2->main;