/usr/local/CPAN/CPANPLUS-Shell-Wx/CPANPLUS/Shell/Wx/ModuleTree.pm



package CPANPLUS::Shell::Wx::ModuleTree;

use Wx qw/wxPD_APP_MODAL wxPD_APP_MODAL wxPD_CAN_ABORT
        wxPD_ESTIMATED_TIME wxPD_REMAINING_TIME wxLIST_AUTOSIZE
        wxVSCROLL wxALWAYS_SHOW_SB wxUPDATE_UI_RECURSE /;
use Wx::Event qw(EVT_CONTEXT_MENU EVT_WINDOW_CREATE EVT_BUTTON
        EVT_TREE_SEL_CHANGED EVT_TREE_ITEM_ACTIVATED EVT_RIGHT_DOWN
        EVT_TREE_ITEM_RIGHT_CLICK);
use Wx::ArtProvider qw/:artid :clientid/;

use Data::Dumper;
use YAML qw/LoadFile Load/;
use File::Spec;
use File::Path;
use Storable;

use threads;
use LWP::Simple;
use Wx::Locale gettext => '_T';

use CPANPLUS::Shell::Wx::util;

#the base class
use base 'Wx::TreeCtrl';

BEGIN {
    use vars qw( @ISA $VERSION);
    @ISA     = qw( Wx::TreeCtrl);
    $VERSION = '0.01';
}

#use some constants to better identify what's going on,
#so we can do stuff like:
#     $self->{'sort'}=(SORTBY)[CATEGORY]; #sort by category

use constant SORTBY => (_T("Author"), _T("Name"), _T("Category"));
use constant {AUTHOR=>0,NAME=>1,CATEGORY=>2};
use constant SHOW => (_T("Installed"),_T("Updated"),_T("New"),_T("All"))   ;
use constant {INSTALLED=>0,UPDATES=>1,NEW=>2,ALL=>3};
use constant MAX_PROGRESS_VALUE => 100000; #the max value of the progressdialogs


sub new {
    my $class = shift;
    my $self  = $class->SUPER::new();    # create an 'empty' TreeCtrl object

    #set default behavior
    $self->{'sort'}=(SORTBY)[CATEGORY]; #DEFAULT: sort by category
#    $self->{'sort'}=(SORTBY)[AUTHOR];   #sort by author
#    $self->{'sort'}=(SORTBY)[NAME];   #sort by module name
    $self->{'show'}=(SHOW)[INSTALLED];
#    $self->{'show'}=(SHOW)[UPDATES];  #DEFAULT: List Updated Modules
#    $self->{'show'}=(SHOW)[NEW];
#    $self->{'show'}=(SHOW)[ALL];

    #this is the thread reference to the info gathering method
    #this is started ad stopped when the listbox's selection is changed
    #threads may be removed in the future or not used at all.
    $self->{_threads}=();

    #setup category names for further use.
    #(they will be used to create hashes)
    $self->{catNames}=[
        _T("Not In Modulelist"),                        _T("Perl Core Modules"),
        _T("Language Extensions"),                        _T("Development Support"),
        _T("Operating System Interfaces"),                _T("Networking Devices, IPC"),
        _T("Data Type Utilities"),                         _T("Database Interfaces"),
        _T("User Interfaces"),                            _T("Language Interfaces"),
        _T("File Names, Systems Locking"),                 _T("String/Language/Text Processing"),
        _T("Options/Arguments/Parameters Processing"),    _T("Internationalization, Locale"),
        _T("Security and Encryption"),                    _T("World Wide Web, HTML, HTTP, CGI"),
        _T("Server and Daemon Utilities"),                _T("Archiving and Compression"),
        _T("Images, Pixmaps, Bitmaps"),                    _T("Mail and Usenet News"),
        _T("Control Flow Utilities"),                    _T("File Handle Input/Output"),
        _T("Microsoft Windows Modules"),                _T("Miscellaneous Modules"),
        _T("Commercial Software Interfaces"),            _T("Bundles"),
        _T("Documentation"),                            _T("Pragma"),
        _T("Perl6")];

    #add the root item.It is hidden.
    $self->AddRoot(_T('Modules'));

    #create links to events
#    EVT_WINDOW_CREATE( $self, $self, \&OnCreate );            #when the tree is created
    EVT_TREE_SEL_CHANGED( $self, $self, \&OnSelChanged);    #when a user changes the selection
    EVT_TREE_ITEM_ACTIVATED($self, $self, \&OnDblClick);    #When the user double-clicks an item
    EVT_TREE_ITEM_RIGHT_CLICK( $self, $self, \&ShowPopupMenu );#when the user wants a pop-up menu
    $self->SetWindowStyleFlag($self->GetWindowStyleFlag()|wxVSCROLL);

    return $self;
}

#this is called when the control is created.
#sub OnCreate {
sub Init {
    my $self = shift;
    my ($event)=@_;

    #get references so we can access them easier
#    $self->{parent}=Wx::Window::FindWindowByName('main_window');
#    $self->{parent}=$self->GetParent();
#    $self->{cpan}=$self->{parent}->{cpan};
#    $self->{config}=$self->{cpan}->configure_object();

    #$self->AssignImageList($imgList);

#    Wx::Window::FindWindowByName('info_prereqs')->AssignImageList($imgList);
    #show info on what we are doing
    Wx::LogMessage _T("Showing "),$self->{'show'},_T(" by "),$self->{'sort'};

    #go ahead and get the list of categories
    $self->{category_list}=$self->_get_categories();

    #$self->{statusBar}=Wx::Window::FindWindowByName('main_window_status');

    #populate tree with default values
    #$self->Populate();

    #$self->{podReader}=$self->{parent}->{podReader} || CPANPLUS::Shell::Wx::PODReader::Frame->new($self);

    $self->SetWindowStyle($self->GetWindowStyleFlag()|wxVSCROLL|wxALWAYS_SHOW_SB);
    _uShowErr;
}

###############################
####### PUBLIC METHODS ########
###############################
#these methods are called from outside to display the relevant modules
sub ShowInstalled{shift->_switch_show(INSTALLED)}
sub ShowUpdated{shift->_switch_show(UPDATES)}
sub ShowNew{shift->_switch_show(NEW)}
sub ShowAll{shift->_switch_show(ALL)}
sub SortByAuthor{shift->_switch_sort(AUTHOR)}
sub SortByName{shift->_switch_sort(NAME)}
sub SortByCategory{shift->_switch_sort(CATEGORY)}

#the following methods are for setting the event handlers for the various
# menu items in the context menu. They all take one parameter:a code ref
#The code ref is then executed with three parameters:
# the menu [Wx::Menu], the event [Wx::CommandEvent], and the name of the selected module
sub SetInfoHandler{$_[0]->{_minfoHandler}=$_[1];}
sub SetInstallMenuHandler{print "Install: ",@_;$_[0]->{_minstallHandler}=$_[1];}
sub SetUpdateMenuHandler{$_[0]->{_mupdateHandler}=$_[1];}
sub SetUninstallMenuHandler{$_[0]->{_muninstallHandler}=$_[1];}
sub SetFetchMenuHandler{$_[0]->{_mfetchHandler}=$_[1];}
sub SetPrepareMenuHandler{$_[0]->{_mprepareHandler}=$_[1];}
sub SetBuildMenuHandler{$_[0]->{_mbuildHandler}=$_[1];}
sub SetTestMenuHandler{$_[0]->{_mtestHandler}=$_[1];}
sub SetExtractMenuHandler{$_[0]->{_mextractHandler}=$_[1];}
sub SetClickHandler{$_[0]->{_clickHandler}=$_[1];}
sub SetDblClickHandler{print "DblClick:",@_,"\n";$_[0]->{_dblClickHandler}=$_[1];}
sub SetStatusBar{$_[0]->{statusBar}=$_[1];}
sub SetMenu{$_[0]->{menu}=$_[1];}
sub GetName{return $_[0]->{thisName}}
sub GetMod{return $_[0]->{thisMod}}
sub SetCPP{$_[0]->{cpan}=$_[1];$_[0]->{config}=$_[1]->configure_object();}

#this is called when the user right-clicks on an item in the tree
sub ShowPopupMenu{
    my $self = shift;
    my ($event)=@_;

    #we can't do any actions on unknown modules
    return if $self->GetItemImage($event->GetItem()) == 4;
    #create the menu
    $menu = CPANPLUS::Shell::Wx::ModuleTree::Menu->new($self,$event->GetItem());
    #show the menu
    $self->PopupMenu($menu,$event->GetPoint());
}


#this is called when the user double-clicks on an item in the tree
sub OnDblClick{
    my $self = shift;
    my ($event)=@_;
    #we can't do any actions on unknown modules
    my $img=$self->GetItemImage($event->GetItem());
    print "Double Click!:".$self->{_dblClickHandler}." img = $img\n";
    return if $img == 4;
    &{$self->{_dblClickHandler}}(@_) if $self->{_dblClickHandler};
}
#this method calls the other methods to populate the tree
sub Populate{
    my $self = shift;

    $self->OnSelChanged();        #clear all values in Info pane
    $self->DeleteAllItems();    #clear all items in tree

    #add the root item with the name of what we are showing
    my $root=$self->AddRoot($self->{'show'});

    #tell the user we are populating the list
    $self->{statusBar}->SetStatusText(_T("Populating List..").$self->{'show'}._T(" By ").$self->{'sort'});

    #call the appropriate method for displaying the modules
    if ($self->{'sort'} eq (SORTBY)[AUTHOR]){
        $self->_show_installed_by_author() if ( $self->{'show'} eq (SHOW)[INSTALLED]);
        $self->_show_updates_by_author() if ( $self->{'show'} eq (SHOW)[UPDATES]);
        $self->_show_new_by_author() if ( $self->{'show'} eq (SHOW)[NEW]);
        $self->_show_all_by_author() if ( $self->{'show'} eq (SHOW)[ALL]);
    }
    if ($self->{'sort'} eq (SORTBY)[NAME]){
        $self->_show_installed_by_name() if ( $self->{'show'} eq (SHOW)[INSTALLED]);
        $self->_show_updates_by_name() if ( $self->{'show'} eq (SHOW)[UPDATES]);
        $self->_show_new_by_name() if ( $self->{'show'} eq (SHOW)[NEW]);
        $self->_show_all_by_name() if ( $self->{'show'} eq (SHOW)[ALL]);
    }
    if ($self->{'sort'} eq (SORTBY)[CATEGORY]){
        $self->_show_installed_by_category() if ( $self->{'show'} eq (SHOW)[INSTALLED]);
        $self->_show_updates_by_category() if ( $self->{'show'} eq (SHOW)[UPDATES]);
        $self->_show_new_by_category() if ( $self->{'show'} eq (SHOW)[NEW]);
        $self->_show_all_by_category() if ( $self->{'show'} eq (SHOW)[ALL]);
    }

    #show any errors generated by CPANPLUS
    _uShowErr;
}

#update only info tab in the lower notebook and clear other items
sub OnSelChanged{
    my ($self,$event)=@_;

    #set global variable for name of what the user selected
    $self->{thisName}=$self->GetItemText($self->GetSelection());
    #set global variable for CPANPLUS::Module object of what the user selected
    $self->{thisMod}=$self->_get_mod($self->{thisName});
    #return if we can't get an object reference
    return unless $self->{thisMod};
    &{$self->{_clickHandler}}($self,$event) if $self->{_clickHandler};
}

#this method check to see which prerequisites have not been met
# We only want recursion when a prereq is NOT installed.
#returns a list of prerequisites, in reverse install order
# i.e. $list[-1] needs to be installed first
sub CheckPrerequisites{
    my $self=shift;
    my $modName=shift;
    my $version=shift||'';
    my $pre=$self->GetPrereqs($modName,$version);
#    print Dumper $pre;
#    return;
    my @updates=();
    foreach $name (@$pre){
        my $mod=$self->_get_mod($name);
        next unless $mod;
        if ($mod->installed_version && $mod->installed_version >= $mod->version){
            $self->{statusBar}->SetStatusText($mod->name." v".$mod->installed_version._T(" is sufficient."));
        }else{
            $self->{statusBar}->SetStatusText($mod->name." v".$mod->installed_version._T(" needs to be updated to ").$name);
            push (@updates,$name);
            push (@updates,$self->CheckPrerequisites($name));
        }
    }
    $self->{statusBar}->SetStatusText('');
    return @updates;
}

#this method fetches the META.yml file from
#search.cpan.org and parses it using YAML.
#It returns the Prerequisites for the given module name
# or the currently selected module, if none given.
# It stores the yml data in the same hierarchy as CPANPLUS
#stores its readme files and other data.
#returns: a list of modules that can be parsed by parse_module()
sub GetPrereqs{
    my $self=shift;
    my $modName=shift || $self->{thisName};
    my $version=shift||'';
#    print "GetPrereqs($modName) \n ";
    my $mod=$self->_get_mod($modName,$version);
#    print $modName.(($version)?"-$version":'')."\n";
#    print Dumper $mod;
    return unless $mod; #if we can't get a module from the name, return

    #set up the directory structure fro storing the yml file
    my $storedDir=_uGetPath($self->{config},'cpp_mod_dir'); #the top-level directory for storing files
    my $author=$mod->author->cpanid; #get the cpanid of the author
    my @split=split('',$author); #split the author into an array so we can:
    my $dest=File::Spec->catdir($storedDir,$split[0],$split[0].$split[1],$author); #extract the first letters
    my $package=$mod->package_name.'-'.$mod->package_version; #name the file appropriately
    $dest=File::Spec->catfile($dest,"$package.yml");
    my $src="http://search.cpan.org/src/$author/$package/META.yml"; #where we are getting the file from

    my $ymldata=''; #the yaml data
    #if we already have this file, read it. Otherwise, get it from web
    if (-e $dest){
        $ymldata=LoadFile($dest);
    }else{
        mkpath($dest,0,0775) unless (-d $dest);        #create the path
        my $yml=getstore($src,$dest) ;                #get and store the yaml file
        $yml=get($src);                                #get the file. TODO add test for existence of yaml file
        $ymldata=Load($yml);                        #load the data
    }

    #return the prequisites
    my $reqs=$ymldata->{'requires'}||{};
    my @ret=();
    foreach $modName (keys(%$reqs)){
        $name=$self->_get_modname($modName,$reqs->{$modName});
#        print "$name-".$reqs->{$key}."\n";
        push(@ret,"$name");
    }

    return \@ret;
}

#appends prequisites the given tree.
#parameters:
#    $module_name, $treeCtrl, $parentNodeInTree = $treeCtrl->GetRootItem
sub _append_prereq{
    my $self=shift;
    my $modName=shift;
    my $preTree=shift||$self;
    my $parentNode=shift || $preTree->GetRootItem();

#    print "_append_prereq($modName)\n";

    my $pre=$self->GetPrereqs($modName);
    #print Dumper $pre;
    foreach $mod (@$pre){
        push (@{$self->{thisPrereq}},$mod) unless ( grep($mod,@{$self->{thisPrereq}}) );
        my $icon=$self->_get_status_icon($mod);
        #print "$mod icon: $icon\n";
        my $pNode=$preTree->AppendItem($parentNode,$mod,$icon);
        $self->_append_prereq($mod,$preTree,$pNode);
    }
}

#this method returns a module for the given name.
# it is OK to pass an existing module ref, as it will
# simply return the ref. You can use this to validate
# all modules and names. You can pass an optional
# boolean denoting whether you would like to return the name
# so parse_module can understand it.
sub _get_modname{
    my ($self,$mod,$version)=@_;
    $version=$version?"-".$version:''; #the version we want

    if (ref($mod) && ($mod->isa('CPANPLUS::Module') or $mod->isa('CPANPLUS::Module::Fake'))){
        if ($version){
            my $name=$mod->name;
            $name =~ s/::/-/g;                                    #parse out the colons in the name
            $mod=$self->{cpan}->parse_module(module=>$name.$version );
        }
            return $mod->package_name;
    }
    $mod =~ s/::/-/g;                                    #parse out the colons in the name
    $mod=$self->{cpan}->parse_module(module=>$mod.$version); #get the module
    return $mod->package_name if $mod;    #return the name if we want to
    return '';
}

sub _get_mod{
    my ($self,$mod,$version)=@_;

    $version=$version?"-".$version:''; #add dash so parse_module can understand

#    print "_get_mod($name,$version,$onlyName)\n";
    #if a module ref is passed, return the ref or the package_name
    if (ref($mod) && ($mod->isa('CPANPLUS::Module') or $mod->isa('CPANPLUS::Module::Fake'))){
        #get new module for $version
        if ($version){
            my $modname=$mod->name;
            $modname =~ s/::/-/g;                                    #parse out the colons in the name
            $mod=$self->{cpan}->parse_module(module=>$modname.$version );
            #return $newMod;
        }
        return $mod;
    }
    $mod =~ s/::/-/g;                                    #parse out the colons in the name
    $mod=$self->{cpan}->parse_module(module=>$mod.$version); #get the module
    return $mod;                                        #return the module object
}
###############################
####### PRIVATE METHODS #######
###############################
#switch the type to show and populate list
#NOTE: These 2 methods are put here to eliminate repetative code
sub _switch_show{
    my ($self,$type) = @_;
    $self->{'show'}=(SHOW)[$type];
    Wx::LogMessage _T("Showing ").$self->{'show'}._T(" Modules");
    $self->Populate();
    _uShowErr;
}
sub _switch_sort{
    my ($self,$type) = @_;
    $self->{'sort'}=(SORTBY)[$type];
    Wx::LogMessage _T("Sorting by ").$self->{'sort'};
    $self->Populate();
    _uShowErr;
}





###############################
######## Module Actions #######
###############################
sub _install_module{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    my $version=shift||'';
    return unless $mod;

    #if no version supplied, check version list in Actions tab
    unless ($version){
        my $versionList=Wx::Window::FindWindowByName('info_distributions');
        $version=$versionList->GetValue() || '';
    }
    my $fullname=$mod->name.'-'.$version;
    $self->{statusBar}->SetStatusText(_T("Installing ").$fullname."...");

    #$mod=$self->{cpan}->parse_module(module => $mod->name.'-'.$version) if $version;
#    print Dumper $mod;
    $self->_install_with_prereqs($mod->name,$version);

    _uShowErr;
}

sub _install_with_prereqs{
    my $self=shift;
    my $modName=shift;
    return unless $modName;
    my $version=shift||'';
    my @prereqs=$self->CheckPrerequisites($modName,$version);
    #print Dumper @prereqs;
    unshift (@prereqs,$modName.($version?"-$version":''));
    my @mods=();            #$self->{cpan}->module_tree(reverse(@prereqs));
    foreach $n (reverse(@prereqs)){
        push @mods, $self->{cpan}->parse_module(module=>$n);
    }

    #print Dumper @mods;
    my $curMod;
    my $isSuccess=1;
    foreach $mod (@mods){
        $curMod=$mod;
        unless ($self->_fetch_module($mod)){$isSuccess=0;last;}
        unless ($self->_extract_module($mod)){$isSuccess=0;last;}
        unless ($self->_prepare_module($mod)){$isSuccess=0;last;}
        unless ($self->_create_module($mod)){$isSuccess=0;last;}
        unless ($self->_test_module($mod)){$isSuccess=0;last;}
        $self->{statusBar}->SetStatusText(_T('Installing ').$mod->name);
        unless ($mod->install){$isSuccess=0;last;}
        $self->{statusBar}->SetStatusText(_T('Successfully installed ').$mod->name);
    }
    #store status info and populate status tab
    $self->_store_status(@mods);
    #$self->_info_get_status();

    unless ($isSuccess){
        $self->{statusBar}->SetStatusText(_T('Failed to install ').$curMod->name._T(". Please Check Log."));
        Wx::MessageBox(_T("Failed to install ").$curMod->name._T("\nCheck Log for more information."));
        return 0;
    }

    _uShowErr;
    return 1;
}

sub _store_status{
    my $self=shift;
    my @mods=@_;
    my $status={};
    my $file=_uGetPath($self->{config},'cpp_stat_file');
    $status=retrieve($file) if (-e $file);
    foreach $mod (@mods){
        $status->{$mod->name}=$mod->status();
    }
    store $status, $file;
}
sub _fetch_module{
    my $self=shift;
    my $mod=shift || $self->{thisMod};
    $mod = $self->{cpan}->parse_module(module=>$mod) unless ($mod->isa('CPANPLUS::Module') || $mod->isa('CPANPLUS::Module::Fake'));
    return unless $mod;
    #print Dumper $mod;
    $self->{statusBar}->SetStatusText(_T('Fetching ').$mod->{'package'});
    my $path=$mod->fetch();
    return 0 unless $path;
    _uShowErr;
    return 1;
}

sub _extract_module{
    my $self=shift;
    my $mod=shift || $self->{thisMod};
    $mod = $self->{cpan}->parse_module(module=>$mod) unless ($mod->isa('CPANPLUS::Module') || $mod->isa('CPANPLUS::Module::Fake'));
    return unless $mod;
    $self->{statusBar}->SetStatusText(_T('Extracting ').$mod->name);
    my $path=$mod->extract();
    return 0 unless $path;
    _uShowErr;
    return 1;
}
sub _prepare_module{
    my $self=shift;
    my $mod=shift || $self->{thisMod};
    $mod = $self->{cpan}->parse_module(module=>$mod) unless ($mod->isa('CPANPLUS::Module') || $mod->isa('CPANPLUS::Module::Fake'));
    return unless $mod;
    $self->{statusBar}->SetStatusText(_T('Preparing ').$mod->name);
    my $path=$mod->prepare();
    return 0 unless $path;
    _uShowErr;
    return 1;
}

sub _create_module{
    my $self=shift;
    my $mod=shift || $self->{thisMod};
    $mod = $self->{cpan}->parse_module(module=>$mod) unless ($mod->isa('CPANPLUS::Module') || $mod->isa('CPANPLUS::Module::Fake'));
    return unless $mod;
    $self->{statusBar}->SetStatusText(_T('Building ').$mod->name);
    my $path=$mod->create();
    return 0 unless $path;
    _uShowErr;
    return 1;
}
sub _test_module{
    my $self=shift;
    my $mod=shift || $self->{thisMod};
    $mod = $self->{cpan}->parse_module(module=>$mod) unless ($mod->isa('CPANPLUS::Module') || $mod->isa('CPANPLUS::Module::Fake'));
    return unless $mod;
    $self->{statusBar}->SetStatusText(_T('Testing ').$mod->name);
    my $path=$mod->test();
    return 0 unless $path;
    _uShowErr;
    return 1;
}
#populates the list with the tree items
#This function takes a tree hash, and optionally a progressdialog or bar
# and the max value of the progress bar
#return 1 on success, or 0 if the user cancelled
# call like:
#$user_has_cancelled = $self->PopulateWithHash(\%tree,[$progress],[$max_pval]);
sub PopulateWithHash{
    #get parameters
    my $self=shift;
    my $tree=shift;
    my $progress=shift;
    my $max_progress=shift;

    #print "Window Height: ".$self->GetClientSize()->GetWidth." , ".$self->GetClientSize()->GetHeight."\n";

    #set defaults.
    #Use half the number of items in the hash as a total items count, if none given
    my $numFound=$tree->{'_items_in_tree_'} || %$tree/2;
    $max_progress=($numFound || 10000) unless $max_progress;

    #create a progressdialog if none specified in params
    $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("Inserting ").$numFound._T(" Items Into Tree..."),
                $numFound,$self,wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME
                ) unless $progress;

    #start timing
    $begin=time();

    #restart count if another progressdialog is passeed in
    $progress->Update(0,_T("Inserting ").$numFound._T(" Items Into Tree..."));
    my $percent=$max_progress/$numFound;
    $cnt=0;

    foreach $top_level ( sort( keys(%$tree) ) ){
        next if $top_level eq '_items_in_tree_';
        my $display=$top_level;
        my $curParent=$self->AppendItem(
            $self->GetRootItem(),
            $top_level,$self->_get_status_icon($top_level));
        foreach $item (sort(@{$tree->{$top_level}})){
            $self->AppendItem($curParent,$top_level."::".$item,$self->_get_status_icon($item)) if ($curParent && $item);
            last unless $progress->Update($cnt*$percent);
            $cnt++;
        }
    }
#	my $dummy=$self->AppendItem($self->GetRootItem(),'end');
#	my $subDummy=$self->AppendItem($dummy,'end');

#    $progress->Update($numFound+1);
    $progress->Destroy();
    my $inserted_time=time()-$begin;
    Wx::LogMessage _T("Finished Inserting in ").sprintf("%d",($inserted_time/60)).":".($inserted_time % 60)."\n";

#    print "Window Height: ".$self->GetClientSize()->GetWidth." , ".$self->GetClientSize()->GetHeight."\n";
    _uShowErr;
    return 1;
}

###############################
######## New By Name ##########
###############################
sub _show_new_by_name{
    my $self=shift;
    if ($self->{'tree_NewByName'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_NewByName'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my %allMods=%{$self->{cpan}->module_tree()}; #get all modules
    my $total=keys(%allMods);
    my $percent=$max_pval/($total||1); #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @allMods - for progressbar
    my $numFound=0;

    $progress->Update(0,_T("Step 1 of 2: Sorting All ").$total._T(" Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $thisName (keys(%allMods)){
        my $i=$allMods{$thisName};
        if (!($i->is_uptodate || $i->installed_version)){
            my ($top_level)=split('::',$thisName);
            push (@{$tree{$top_level}}, ($thisName eq $top_level)?():$thisName); #add the item to the tree
            $numFound++;
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_NewByName'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    _uShowErr;
    return 1;
}
###############################
######## New By Author ########
###############################
sub _show_new_by_author{
    my $self=shift;
    if ($self->{'tree_NewByAuthor'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_NewByAuthor'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my %allMods=%{$self->{cpan}->module_tree()}; #get all modules
    my $total=keys(%allMods);
    my $percent=$max_pval/($total||1); #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @allMods - for progressbar
    $numFound=0;

    $progress->Update(0,_T("Step 1 of 2: Categorizing All ").$total._T(" Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $thisName (keys(%allMods)){
        my $i=$allMods{$thisName};
        if (!($i->is_uptodate || $i->installed_version)){
            my $thisAuthor=$i->author()->cpanid." [".$i->author()->author."]";
            my $cat_num=$self->{category_list}->{$thisName};
            push (@{$tree{$thisAuthor}}, $thisName); #add the item to the tree
            $numFound++;
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_NewByAuthor'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    _uShowErr;
    return 1;
}

###############################
######## New By Category ######
###############################
sub _show_new_by_category{
    my $self=shift;
    if ($self->{'tree_NewByCategory'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_NewByCategory'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),$max_pval,$self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);

    my %allMods=%{$self->{cpan}->module_tree()}; #get all modules
    my $total=keys(%allMods);
    my $percent=$max_pval/($total||1); #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @allMods - for progressbar
    $numFound=0;

    $progress->Update(0,_T("Step 1 of 2: Categorizing All ").$total.(" Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $thisName (keys(%allMods)){
        my $i=$allMods{$thisName};
        my $cat_num=$self->{category_list}->{$thisName};
        if (defined($cat_num) && !($i->is_uptodate || $i->installed_version)){
            $cat_num=0 if ($cat_num==99); #don't use index 99, it make array too large
            $cat_num=1 if ($i->module_is_supplied_with_perl_core() && $cat_num==2);
            push (@{$tree{$self->{catNames}->[$cat_num]}}, $thisName); #add the item to the tree
            $numFound++;
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }

    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_NewByCategory'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    _uShowErr;
    return 1;
}

###############################
######## All By Name ##########
###############################
sub _show_all_by_name{
    my $self=shift;
    if ($self->{'tree_AllByName'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_AllByName'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my %allMods=%{$self->{cpan}->module_tree()}; #get all modules
    my $total=keys(%allMods);
    my $percent=$max_pval/($total||1); #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @allMods - for progressbar

    $progress->Update(0,_T("Step 1 of 2: Sorting All ").$total._T(" Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $thisName (keys(%allMods)){
        my $i=$allMods{$thisName};
        my ($top_level)=split('::',$thisName);
        push (@{$tree{$top_level}}, ($thisName eq $top_level)?():$thisName); #add the item to the tree
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$total;
    $self->{'tree_AllByName'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    _uShowErr;
    return 1;
}

###############################
######## All By Author ########
###############################
sub _show_all_by_author{
    my $self=shift;
    if ($self->{'tree_AllByAuthor'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_AllByAuthor'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my %allMods=%{$self->{cpan}->module_tree()}; #get all modules
    my $total=keys(%allMods);
    my $percent=$max_pval/($total||1); #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @allMods - for progressbar

    $progress->Update(0,_T("Step 1 of 2: Categorizing All ").$total._T(" Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $thisName (keys(%allMods)){
        my $i=$allMods{$thisName};
        my $thisAuthor=$i->author()->cpanid." [".$i->author()->author."]";
        my $cat_num=$self->{category_list}->{$thisName};
        push (@{$tree{$thisAuthor}}, $thisName); #add the item to the tree
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$total;
    $self->{'tree_AllByAuthor'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    _uShowErr;
    return 1;
}

###############################
###### All By Category ########
###############################
sub _show_all_by_category{
    my $self=shift;
    if ($self->{'tree_AllByCategory'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_AllByCategory'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),$max_pval,$self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);

    my %allMods=%{$self->{cpan}->module_tree()}; #get all modules
    my $total=keys(%allMods);
    my $percent=$max_pval/($total||1); #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @allMods - for progressbar

    $progress->Update(0,_T("Step 1 of 2: Categorizing All ").$total._T(" Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $thisName (keys(%allMods)){
        my $i=$allMods{$thisName};
        my $cat_num=$self->{category_list}->{$thisName};
        if (defined($cat_num)){
            $cat_num=0 if ($cat_num==99); #don't use index 99, it make array too large
            $cat_num=1 if ($i->module_is_supplied_with_perl_core() && $cat_num==2);
            push (@{$tree{$self->{catNames}->[$cat_num]}}, $thisName); #add the item to the tree
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }

    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$total;
    $self->{'tree_AllByCategory'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;
}


sub _show_updates_by_category{
    my $self=shift;
    if ($self->{'tree_UpdatesByCategory'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_UpdatesByCategory'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),$max_pval,$self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);

    my @installed=$self->{cpan}->installed(); #get installed modules
    my $percent=$max_pval/@installed; #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @installed - for progressbar
    my $numFound=0; #the number of modules that match CPAN to CPANPLUS::Installed
    $progress->Update(0,_T("Step 1 of 2: Categorizing ").@installed._T(" Installed Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $i (@installed){
        unless ($i->is_uptodate()){
            my $thisName=$i->name;
            my $cat_num=$self->{category_list}->{$thisName};
            if (defined($cat_num)){
                $cat_num=0 if ($cat_num==99); #don't use index 99, it make array too large
                $cat_num=1 if ($i->module_is_supplied_with_perl_core() && $cat_num==2);
                push (@{$tree{$self->{catNames}->[$cat_num]}}, $thisName); #add the item to the tree
                $numFound++; #increment the number of items that matched
            }
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }

    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_UpdatesByCategory'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;
}

sub _show_updates_by_author{
    my $self=shift;
    if ($self->{'tree_UpdatesByAuthor'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_UpdatesByAuthor'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my @installed=$self->{cpan}->installed(); #get installed modules
    my $percent=$max_pval/@installed; #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @installed - for progressbar
    my $numFound=0; #the number of modules that match CPAN to CPANPLUS::Installed
    $progress->Update(0,_T("Step 1 of 2: Sorting ").@installed." Installed Modules..."); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $i (@installed){
        unless ($i->is_uptodate()){
            my $thisName=$i->name;
            my $thisAuthor=$i->author()->cpanid." [".$i->author()->author."]";
            my $cat_num=$self->{category_list}->{$thisName};
            push (@{$tree{$thisAuthor}}, $thisName); #add the item to the tree
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_UpdatesByAuthor'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;
}


sub _show_updates_by_name{
    my $self=shift;
    if ($self->{'tree_UpdatesByName'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_UpdatesByName'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my @installed=$self->{cpan}->installed(); #get installed modules
    my $percent=$max_pval/@installed; #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @installed - for progressbar
    my $numFound=0; #the number of modules that match CPAN to CPANPLUS::Installed
    $progress->Update(0,_T("Step 1 of 2: Sorting ").@installed." Installed Modules..."); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $i (@installed){
        unless ($i->is_uptodate()){
            my $thisName=$i->name;
            my ($top_level)=split('::',$thisName);
            push (@{$tree{$top_level}}, ($thisName eq $top_level)?():$thisName); #add the item to the tree
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_UpdatesByName'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;
}


sub _show_installed_by_name{
    my $self=shift;
    if ($self->{'tree_InstalledByName'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_InstalledByName'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my @installed=$self->{cpan}->installed(); #get installed modules
    my $percent=$max_pval/@installed; #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @installed - for progressbar
    my $numFound=0; #the number of modules that match CPAN to CPANPLUS::Installed
    $progress->Update(0,_T("Step 1 of 2: Sorting ").@installed._T(" Installed Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $i (@installed){
        my $thisName=$i->name;
        my ($top_level)=split('::',$thisName);
        push (@{$tree{$top_level}}, ($thisName eq $top_level)?():$thisName); #add the item to the tree
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=keys(%tree); #@installed; #$numFound;
    $self->{'tree_InstalledByName'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;
}

#populate tree with installed modules sorted by author id
sub _show_installed_by_author{
    my $self=shift;
    if ($self->{'tree_InstalledByAuthor'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_InstalledByAuthor'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new("Setting Up List...",
                "CPANPLUS is getting information...",
                $max_pval,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my @installed=$self->{cpan}->installed(); #get installed modules
    my $percent=$max_pval/@installed; #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @installed - for progressbar
    my $numFound=0; #the number of modules that match CPAN to CPANPLUS::Installed
    $progress->Update(0,_T("Step 1 of 2: Sorting ").@installed._T(" Installed Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $i (@installed){
        my $thisName=$i->name;
        my $thisAuthor=$i->author()->cpanid." [".$i->author()->author."]";
        my $cat_num=$self->{category_list}->{$thisName};
        push (@{$tree{$thisAuthor}}, $thisName); #add the item to the tree
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_InstalledByAuthor'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;
}

#populate tree with installed modules sorted by category
sub _show_installed_by_category{
    my $self=shift;
    if ($self->{'tree_InstalledByCategory'}){
        return 0 unless $self->PopulateWithHash($self->{'tree_InstalledByCategory'});
        Wx::LogMessage _T("[Done]");
        return 1;
    }
    my %tree=();
    my $max_pval=10000;  #the maximum value of the progress bar
    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                10000,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);

    my @installed=$self->{cpan}->installed(); #get installed modules
    my $percent=$max_pval/@installed; #number to increment progress by
    my $begin=time(); #for timing loops
    my $cnt=0;  #the count of current index of @installed - for progressbar
    my $numFound=0; #the number of modules that match CPAN to CPANPLUS::Installed
    $progress->Update(0,_T("Step 1 of 2: Categorizing ").@installed._T(" Installed Modules...")); #start actual progress

    #search through installed modules and insert them into the correct category
    foreach $i (@installed){
        my $thisName=$i->name;
        my $cat_num=$self->{category_list}->{$thisName};
        $progress->Update($cnt*$percent);
#            "Step 1 of 2: Categorizing ".@installed." Installed Modules...#$cnt : ".$i->name);
        if (defined($cat_num)){
            $cat_num=0 if ($cat_num==99); #don't use index 99, it make array too large
            $cat_num=1 if ($i->module_is_supplied_with_perl_core() && $cat_num==2);
            push (@{$tree{$self->{catNames}->[$cat_num]}}, $thisName); #add the item to the tree
            $numFound++; #increment the number of items that matched
        }
        unless ($progress->Update($cnt*$percent)){
            $progress->Destroy();
            return 0;
        }
        $cnt++; #increment current index in @installed
    }
    #end timing method
    my $end=time();
    Wx::LogMessage _T("Finished Sorting in ").sprintf("%d",(($end-$begin)/60)).":".(($end-$begin) % 60)."\n";

    #store tree for later use
    $tree{'_items_in_tree_'}=$numFound;
    $self->{'tree_InstalledByCategory'}=\%tree;

    #populate the TreeCtrl
    return 0 unless $self->PopulateWithHash(\%tree,$progress,$max_pval);

    Wx::LogMessage _T("[Done]");
    $progress->Destroy();
    _uShowErr;
    return 1;

}

#this returns a referece to a hash, (module_name=>category_number), of all modules
sub _get_categories{
    my $self = shift;

    my $moduleFile= _uGetPath($self->{config},'cpp_modlist');
    my $modlistEval;  #the string to evaluate == 03modlist.data.gz

    #inflate file into $modlistEval
    Wx::LogMessage _T("Getting Category List...Inflating...");
    use IO::Uncompress::AnyInflate qw(anyinflate $AnyInflateError) ;
    anyinflate $moduleFile => \$modlistEval
        or Wx::LogMessage _T("anyinflate failed: ").$AnyInflateError."\n";
    return unless $modlistEval;
    Wx::LogMessage _T("Successfully Inflated Module Info File!");

    #get rid of file info in header
    $modlistEval=~s/(.*)package CPAN\:\:Modulelist/package CPAN\:\:Modulelist/si ;#get rid of file info

    #create List of Categories
    my $cat_hash=(); #the hash that is stored in the file
    my %categories=(); #the return value of this function
    eval $modlistEval.'$cat_hash=(CPAN::Modulelist->data)[0];';Wx::LogMessage($@) if $@;
       $categories{$_}=$cat_hash->{$_}->{'chapterid'} foreach (keys(%$cat_hash));

    #return list
    Wx::LogMessage _T("Successfully read Category List!");
    return \%categories;
    _uShowErr;
}

#this method displays the search results.
#use: $self->search($type,@list_of_searches)
#TODO add support for multiple searches, using ',' as delimiter
#TODO show scrollbars
sub search{
    my $self=shift;
    my ($type,@search)=@_;
    $self->{statusBar}->SetStatusText(_T("Searching. Please Wait..."));

    my $progress=Wx::ProgressDialog->new(_T("Setting Up List..."),
                _T("CPANPLUS is getting information..."),
                MAX_PROGRESS_VALUE,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);

    $self->DeleteChildren($self->GetRootItem());
    foreach $s (@search){
        Wx::LogMessage _T("Searching for: ").$search[0]._T(" by $type\n");
        if ($s=~m|/(.*)/(.*)|){
            #print "Matching Regex...\n";
            eval "\$s=qr/$1/".($2||'');
        }else{
            $s=qr/$s/i;
        }
    }
    $type= lc($type);

    my $mparent=$self->GetRootItem();
    my @names=();
    my $numFound=0;
    my $tmpCnt=1;
    $modules={};
    if ($type eq 'any' || $type eq 'all'){
        my @modterms=CPANPLUS::Module::accessors(); #('name','version','path','comment','package','description','dslip','status');
        my @authterms=CPANPLUS::Module::Author::accessors(); #('author','cpanid','email');
        my $percent = MAX_PROGRESS_VALUE/(@modterms+@authterms);
        my $count=0;
        foreach $term (@modterms){
            if ($progress->Update($percent*($count++),_T("Searching in $term: Found ").keys(%$modules)._T(" items"))){
                foreach $m ($self->{cpan}->search(type => $term, allow => \@search)){
                    if ($m->isa(CPANPLUS::Module)){
                        #print "module: ".$m->name." [".($percent*($count++)/MAX_PROGRESS_VALUE)."]\n";
                        $modules->{$m->name} = $m;
                    }
                    if ($m->isa(CPANPLUS::Module::Author)){
                        foreach $amod ($m->modules()){
                            #print "amodule: ".$m->name." [".($percent*($count++)/MAX_PROGRESS_VALUE)."]\n";
                            $modules->{$amod->name} = $amod;
                        }
                    }
                }
            }else{$progress->Destroy();return;}
        }
    }else{
        foreach $m ($self->{cpan}->search(type => $type, allow => \@search)){
            return unless $progress->Update(MAX_PROGRESS_VALUE-1,_T("Found ").keys(%$modules)._T(" items"));
            $modules->{$m->name}=$m;
        }
    }

    $self->PopulateWithModuleHash($progress,$modules);
    $progress->Destroy;


    #Wx::Window::FindWindowByName('module_splitter')->FitInside();
    #Wx::Window::FindWindowByName('module_splitter')->UpdateWindowUI(wxUPDATE_UI_RECURSE );

    _uShowErr;
    print "Window Height: ".$self->GetClientSize()->GetWidth." , ".$self->GetClientSize()->GetHeight."\n";
#    print Dumper $self->GetClientSize();
    $self->{statusBar}->SetStatusText('');
    my $curStyle=$self->GetWindowStyleFlag();
    $self->SetWindowStyleFlag($self->GetWindowStyleFlag()|wxVSCROLL);
    $self->GetParent()->SetWindowStyleFlag($self->GetParent()->GetWindowStyleFlag()|wxVSCROLL);
}

sub PopulateWithModuleHash{
    my $self=shift;
    my $progress=shift || Wx::ProgressDialog->new(_T("Please Wait..."),
                _T("Displaying List..."),
                MAX_PROGRESS_VALUE,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my $modules=shift;
    my @names=();
    my $count=0;
    my $numFound=keys(%$modules);
    return unless $numFound>0;
    my $percent=MAX_PROGRESS_VALUE / $numFound;
    return unless $progress->Update(0,_T("Getting info for $numFound items."));

	my $newTree={};
    #get information from modules
    foreach $modname (keys(%$modules)){
        last unless $progress->Update($percent*$count);
        if ($modules->{$modname}->isa('CPANPLUS::Module')){
            my @names=split('::',$modname);
            my $ref=$newTree;
            foreach $n (@names){
            	$ref=$ref->{$n}='';
#            	push(@names,$modname);
            }
        }
        if ($modules->{$modname}->isa('CPANPLUS::Module::Author')){
            foreach $m ($modules->{$modname}->modules()){
            	my @names=split('::',$m->name);
            	my $ref=$newTree;
	            foreach $n (@names){
	            	$ref=$ref->{$n}='';
#		            push(@names,$m->name);
	            }
            }
        }
        $count++;
    }

    #populate the tree ctrl
    return unless $progress->Update(0,_T("Populating tree with ").$numFound._T(" items.") );
    $count=0;
#	my $dummy=$self->AppendItem($self->GetRootItem(),'Modules');    
#   foreach $item (sort {lc($a) cmp lc($b)} @names){
#        $self->AppendItem($dummy,$item,$self->_get_status_icon($item));
#        $count++;
#	}	
	foreach $k (sort(keys(%$newTree))){
        return unless $progress->Update($percent*$count);
		my $parent=$self->AppendItem($self->GetRootItem(),$item,$self->_get_status_icon($item));
		my $ref=$newTree->{$k};
		while($ref){
			
		}
        $count++;
    }
#	my $dummy=$self->AppendItem($self->GetRootItem(),'end');
#	my $subDummy=$self->AppendItem($dummy,'end');
#	$self->EnsureVisible($dummy);
    return 1;
}

#this method populates the list with the given module objects.
#if the object is an Author, then get the module names he/she has written
sub PopulateWithModuleList{
    my $self=shift;
    my $progress=shift || Wx::ProgressDialog->new(_T("Please Wait..."),
                _T("Displaying List..."),
                MAX_PROGRESS_VALUE,
                $self,
                wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME);
    my $totalFound=shift;
    return unless $totalFound;
    my $numFound=$totalFound;
    my @modules=@_;
    my @names=();
    my $count=0;
    my $percent=MAX_PROGRESS_VALUE/$totalFound;
    return unless $progress->Update(0,_T("Getting info for $numFound items."));

    #get information from modules
    foreach $mod (@modules){
        last unless $progress->Update($percent*$count);
        if ($mod->isa('CPANPLUS::Module')){
            push(@names,$mod->name);
        }
        if ($mod->isa('CPANPLUS::Module::Author')){
            foreach $m ($mod->modules()){
                push(@names,$m->name);
            }
        }
        $count++;
    }

    #populate the tree ctrl
    return unless $progress->Update(0,_T("Populating tree with").$totalFound._T(" items.") );
    $count=0;
    foreach $item (sort {lc($a) cmp lc($b)} @names){
        return unless $progress->Update($percent*$count);
        $self->AppendItem($self->GetRootItem(),$item,$self->_get_status_icon($item));
        $count++;
    }
    return 1;
}

#this method returns the index in the imageList for the status of the passed name
sub _get_status_icon{
    my $self=shift;
    my ($name)=@_;
    my $mod=$self->_get_mod($name);
    return $self->{iconList}->unknown->idx unless $mod;
    return $self->{iconList}->installed->idx if $mod->is_uptodate();
    return $self->{iconList}->not_installed->idx if !$mod->installed_version();
    return $self->{iconList}->update->idx;

    _uShowErr;

}
sub SetImageList{                                #must be a Wx::ImageList
    my ($self,$list)=@_;
    $self->{iconList}=$list;
    $self->AssignImageList($list->imageList);
}

########################################
########### Context Menu ##############
########################################



package CPANPLUS::Shell::Wx::ModuleTree::Menu;
use base 'Wx::Menu';
use Wx::Event qw/EVT_WINDOW_CREATE EVT_MENU/;
use Data::Dumper;
use Wx::Locale gettext => '_T';

sub new {
    my $class = shift;
    my $parent=shift;
    my $item=shift;
    my $self  = $class->SUPER::new();    # create an 'empty' menu object
    #get image so we can determine what the status is
    $img=$parent->GetItemImage($item);
    $actions=new Wx::Menu();
    $install=$actions->Append(1000,_T("Install")) if $img == 3;
    $update=$actions->Append(1001,_T("Update")) if $img == 1;
    $uninstall=$actions->Append(1002,_T("Uninstall")) if ($img==0 or $img==1);
    $actions->AppendSeparator();
    $fetch=$actions->Append(1003,_T("Fetch"));
    $extract=$actions->Append(1004,_T("Extract"));
    $prepare=$actions->Append(1005,_T("Prepare"));
    $build=$actions->Append(1006,_T("Build"));
    $test=$actions->Append(1007,_T("Test"));

    $self->AppendSubMenu($actions,_T("Actions"));

    $info=$self->Append(1008,_T("Get All Information"));

    my $modName=$parent->GetItemText($item);

    EVT_MENU( $self, $info, sub{&{$parent->{_minfoHandler}}(@_,$modName)} ) if $parent->{_minfoHandler};
    EVT_MENU( $actions, $install, sub{&{$parent->{_minstallHandler}}(@_,$modName)} ) if ($img == 3 && $parent->{_minstallHandler});
    EVT_MENU( $actions, $update, sub{&{$parent->{_mupdateHandler}}(@_,$modName)} ) if ($img == 1 && $parent->{_mupdateHandler});
    EVT_MENU( $actions, $uninstall, sub{&{$parent->{_muninstallHandler}}(@_,$modName)} )if (($img==0 or $img==1) && $parent->{_muninstallHandler});
    EVT_MENU( $actions, $fetch, sub{&{$parent->{_mfetchHandler}}(@_,$modName)} )  if $parent->{_mfetchHandler};
    EVT_MENU( $actions, $prepare, sub{&{$parent->{_mprepareHandler}}(@_,$modName)} ) if $parent->{_mprepareHandler};
    EVT_MENU( $actions, $build, sub{&{$parent->{_mbuildHandler}}(@_,$modName)} ) if $parent->{_mbuildHandler};
    EVT_MENU( $actions, $test,sub{&{$parent->{_mtestHandler}}(@_,$modName)} ) if $parent->{_mtestHandler};
    EVT_MENU( $actions, $extract, sub{&{$parent->{_mextractHandler}}(@_,$modName)} ) if $parent->{_mextractHandler};
#    print "Ending ";
    return $self;
}



1;