The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package CPANPLUS::Shell::Wx::ModulePanel;
use Wx qw[:everything];
use base qw(Wx::Panel);
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/;

#since we want to route calls from here to the module tree,
our $AUTOLOAD;

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;

sub new{
    my( $self, $parent, $id, $pos, $size, $style, $name ) = @_;
    $parent = undef              unless defined $parent;
    $id     = -1                 unless defined $id;
    $pos    = wxDefaultPosition  unless defined $pos;
    $size   = wxDefaultSize      unless defined $size;
    $name   = ""                 unless defined $name;
    $style  = wxTAB_TRAVERSAL    unless defined $style;

    $self = $self->SUPER::new( $parent, $id, $pos, $size, $style, $name );
    print "New ModulePanel\n";
    return $self;
}

#initialize all the children. This was the OnWindowCreate Handler
sub Init {
    my $self = shift;

    #get references so we can access them easier
    $self->{parent}=$self->GetParent();        #Wx::Window::FindWindowByName('main_window');
    $self->{mod_tree}=Wx::Window::FindWindowByName('tree_modules');
    $self->{mod_tree}->Init();
    #show info on what we are doing
    Wx::LogMessage _T("Showing "),$self->{'show'},_T(" by "),$self->{'sort'};

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

    #print Dumper $self->{mod_tree};
    #for testing purposes, insert test values
    my @testMods=qw/Alter CPAN Cache::BerkeleyDB CPANPLUS Module::NoExist Muck Acme::Time::Baby Wx/;
        foreach $item (sort(@testMods)){
            $self->{mod_tree}->AppendItem($self->{mod_tree}->GetRootItem(),$item,$self->{mod_tree}->_get_status_icon($item));
        }

    $self->_setup_search();
    $self->_setup_info_tabs();

    #my $cMenu=$self->{mod_tree}->GetContextMenu();
#    $self->{mod_tree}->SetCPP(\&HandleContextInfo);
    $self->{mod_tree}->SetInfoHandler(\&HandleContextInfo);
    $self->{mod_tree}->SetClickHandler( sub{$self->HandleTreeClick(@_)});
    #$self->{mod_tree}->SetDblClickHandler( sub{$self->ShowPODReader(@_)});

    _uShowErr;
}
#set up the search bar.
sub _setup_search{
    $self=shift;
    my $searchbox=Wx::Window::FindWindowByName('cb_main_search');
    my $typebox=Wx::Window::FindWindowByName('cb_search_type');
    EVT_TEXT_ENTER( $self,
        Wx::XmlResource::GetXRCID('cb_main_search'),
        sub{$self->{list}->search($typebox->GetValue,$searchbox->GetValue);} );

    my @items=();

    foreach $term (CPANPLUS::Module->accessors()){
        $term=~s/^\_//;
        push(@items,$term) unless grep(/$term/,@items) ;
    }
    foreach $term (CPANPLUS::Module::Author->accessors()){
        $term=~s/^\_//;
        push(@items,$term) unless grep(/$term/,@items) ;
    }
    $typebox->Append(ucfirst($_)) foreach (sort(@items));
    #$typebox->SetValue(0);
}


sub _setup_info_tabs{
    $self=shift;
    #attach menu events
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_more_info'),sub{$self->_get_more_info()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_readme'),sub{$self->_info_get_readme()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_status'),sub{$self->_info_get_status()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_prereqs'),sub{$self->_info_get_prereqs()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_files'),sub{$self->_info_get_files()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_versions'),sub{$self->_info_get_versions()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_contents'),sub{$self->_info_get_contents()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_report_this'),sub{$self->_info_get_report_this()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_report_all'),sub{$self->_info_get_report_all()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_get_validate'),sub{$self->_info_get_validate()} );

    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_fetch'),sub{$self->{mod_tree}->_fetch_module()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_extract'),sub{$self->{mod_tree}->_extract_module()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_prepare'),sub{$self->{mod_tree}->_prepare_module()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_create'),sub{$self->{mod_tree}->_create_module()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_test'),sub{$self->{mod_tree}->_test_module()} );
    EVT_BUTTON( $self, Wx::XmlResource::GetXRCID('info_install'),sub{$self->{mod_tree}->_install_module()} );

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

    #set global variable for CPANPLUS::Module object of what the user selected
    $self->{thisName}=$tree->GetName;
    $self->{thisMod}=$self->_get_mod($self->{thisName});

    #reset all info in Info pane
    $self->_info_reset();

    #return if we can't get an object reference
    return unless $self->{thisMod};

    #display info
    $self->_info_get_info();
}


# Here, we reroute the calls to ModuleTree
sub AUTOLOAD{
    my $self=shift;
    my @ops=@_;
    my $type = ref($self) or return undef;
    my $func=$AUTOLOAD;
    #print "$func (".ref($func).") not found in ModulePanel. Trying ModuleTree...\n";
    $func =~ s/.*:://;
    if ($self->{mod_tree}->can($func)){
        @ops=map( ((defined($_))?$_:'undef'),@ops); #make sure undefs are kept undef
        return $self->{mod_tree}->$func(@ops);
    }elsif ($self->{mod_tree}->{$func}){
        return $self->{mod_tree}->{$func};
    }else{
        Wx::LogError("$func does not exist!");
    }

}

sub HandleContextInfo{
    my ($self,$menu,$cmd_event,$modName)=@_;
    my $modtree=Wx::Window::FindWindowByName('tree_modules');

    $modtree->_get_more_info($self->{cpan}->parse_tree(module=>$modName));
}

#accessors (should be obvious)
sub SetStatusBar{$_[0]->{statusBar}=$_[1];$_[0]->{mod_tree}->SetStatusBar($_[1]);}        #any widget that has SetStatusText($txt) method
sub GetStatusBar{return $_->{statusbar};}
sub SetPODReader{$_[0]->{PODReader}=$_[1];}        #must be a CPANPLUS::Shell::Wx::PODReader
sub GetPODReader{return $_->{PODReader};}
sub SetImageList{                                #must be a Wx::ImageList
    my ($self,$imgList)=@_;
    $self->{imageList}=$imgList;
    $self->{mod_tree}->SetImageList($imgList);
    print "Assigning imagelist....".$imgList->imageList."\n";
    Wx::Window::FindWindowByName('info_prereqs')->AssignImageList($imgList->imageList);
}
sub GetImageList{return $_->{imageList};}
sub SetModuleTree{$_[0]->{mod_tree}=$_[1];}        #must be a CPANPLUS::Shell::Wx::ModuleTree
sub GetModuleTree{return $_->{mod_tree};}
sub SetCPP{                                        #must be a CPANPLUS::Backend
    my $self=shift;
    my $cpp=shift;
    $self->{cpan}=$cpp;
    $self->{config}=$cpp->configure_object;
    $self->{mod_tree}->{cpan}=$cpp if $self->{mod_tree};
    $self->{mod_tree}->{config}=$cpp->configure_object if $self->{mod_tree};
}
sub GetCPP{return $_[0]->{cpan};}
sub SetConfig{$_[0]->{config}=$_[1];}            #must be a CPANPLUS::Backend::Configure
sub GetConfig{return $_[0]->{config};}
#sub _get_mod{shift->{mod_tree}->_get_mod(@_)}
#sub _get_modname{shift->{mod_tree}->_get_modname(@_)}
#sub SetDblClickHandler{$_[0]->{mod_tree}->SetDblClickHandler($_[1]);}

##################################
##### Moved from ModuleTree #####
##################################

#get prereqs and fills in Prereqs tab
sub _info_get_prereqs{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    my $version=shift||'';

    return unless $mod;
    #set up variables for retrieving and setting data
    $self->{thisPrereq}=[];

    #get correct control and clear all items
    my $preTree=Wx::Window::FindWindowByName('info_prereqs');
    $preTree->DeleteAllItems();
    my $root=$preTree->AddRoot('prereqs');

    #append all prerequisites to root item
    $self->{mod_tree}->_append_prereq($self->_get_mod($mod,$version),$preTree,$root);

    #show any CPANPLUS errors in Log tab
    _uShowErr;
}

#clears all info fields. Optionally takes a tab name to clear only that tab's fields.
sub _info_reset{
    my $self=shift;
    my $context=shift;

    Wx::Window::FindWindowByName('info_tab_text')->SetValue('') unless $self->{thisName};
    Wx::Window::FindWindowByName('info_tab_text')->SetValue($self->{thisName}._T(" may not exist!")) if (!$context || $context eq 'info');
    Wx::Window::FindWindowByName('info_report')->DeleteAllItems() if (!$context || $context eq 'report');
    Wx::Window::FindWindowByName('info_prereqs')->DeleteAllItems() if (!$context || $context eq 'prereqs');
    Wx::Window::FindWindowByName('info_validate')->Clear() if (!$context || $context eq 'validate');
    Wx::Window::FindWindowByName('info_files')->Clear() if (!$context || $context eq 'files');
    Wx::Window::FindWindowByName('info_contents')->Clear() if (!$context || $context eq 'contents');
    Wx::Window::FindWindowByName('info_readme')->Clear() if (!$context || $context eq 'readme');
    Wx::Window::FindWindowByName('info_distributions')->Clear() if (!$context || $context eq 'readme');
    if (!$context || $context eq 'status'){
        Wx::Window::FindWindowByName('info_status_installed')->SetValue(0);
        Wx::Window::FindWindowByName('info_status_uninstall')->SetValue(0);
        Wx::Window::FindWindowByName('info_status_fetch')->SetValue('');
        Wx::Window::FindWindowByName('info_status_signature')->SetValue(0);
        Wx::Window::FindWindowByName('info_status_extract')->SetValue('');
        Wx::Window::FindWindowByName('info_status_created')->SetValue(0);
        Wx::Window::FindWindowByName('info_status_installer_type')->SetValue('');
        Wx::Window::FindWindowByName('info_status_checksums')->SetValue('');
        Wx::Window::FindWindowByName('info_status_checksum_value')->SetValue('');
        Wx::Window::FindWindowByName('info_status_checksum_ok')->SetValue(0);
    }
}

sub _get_more_info{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;

    $self->{statusBar}->SetStatusText(_T("Getting Status for ").$mod->name."...");

    $progress=Wx::ProgressDialog->new(_T("Getting Extended Info..."),
                _T("Updating List of Files..."),
                8,$self,wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ESTIMATED_TIME|wxPD_REMAINING_TIME
                );
    $self->_info_get_versions($mod) if $progress->Update(4,_T("Getting Version Information..."));
    $self->_info_get_files($mod)  if $progress->Update(0);
    $self->_info_get_readme($mod) if $progress->Update(1,_T("Getting README..."));
    $self->_info_get_status($mod) if $progress->Update(2,_T("Getting Status for ").$self->{thisName}."...");
    $self->_info_get_prereqs($mod) if $progress->Update(3,_T("Getting Prerequisites for ").$self->{thisName}."...");
    $self->_info_get_contents($mod) if $progress->Update(5,_T("Getting Contents..."));
    $self->_info_get_report_all($mod) if $progress->Update(6,_T("Getting Reports..."));
    $self->_info_get_validate($mod) if $progress->Update(7,_T("Validating Module..."));

    $self->{statusBar}->SetStatusText('');
    $progress->Destroy();
    _uShowErr;

}

sub _info_get_files{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;

    $self->{statusBar}->SetStatusText(_T("Getting File Info..."));
    my $info_files=Wx::Window::FindWindowByName('info_files');
    $info_files->Clear();

    my @files=$mod->files();
    my $text=$mod->name._T(" has ").(@files || _T('NO'))._T(" installed files:\n");
    foreach $file (@files){
        $text.="$file\n";
    }
    $text.=_T("There was a problem retrieving the file information for this module.\n").
        ("Please see the log for more info.\n") unless @files;
    $info_files->AppendText($text);
    _uShowErr;
}
sub _info_get_info{
    my $self=shift;
    my $mod=shift||$self->{mod_tree}->GetMod();
    #return unless $mod;

    my $info_ctrl=Wx::Window::FindWindowByName('info_tab_text');
    $info_ctrl->Clear();
    $self->{statusBar}->SetStatusText(_T("Getting Info for ").$mod->name."...");

    my $status_info_text='';
    #update info panel
    unless ($mod){
        $info_ctrl->AppendText(_T("No Information Found!"));
    }else{
        my $info=$mod->details();
         $status_info_text.=_T("\tAuthor\t\t\t\t").$info->{'Author'}."\n" if $info->{'Author'};
         $status_info_text.=_T("\tDescription\t\t\t").$info->{'Description'}."\n" if $info->{'Description'};
         $status_info_text.=_T("\tIs Perl Core?\t\t\t").($mod->package_is_perl_core()?_T('Yes'):_T('No'))."\n";
         $status_info_text.=_T("\tDevelopment Stage\t").$info->{'Development Stage'}."\n" if $info->{'Development Stage'};
         $status_info_text.=_T("\tInstalled File\t\t\t").$info->{'Installed File'}."\n" if $info->{'Installed File'};
         $status_info_text.=_T("\tInterface Style\t\t").$info->{'Interface Style'}."\n" if $info->{'Interface Style'};
          $status_info_text.=_T("\tLanguage Used\t\t").$info->{'Language Used'}."\n" if $info->{'Language Used'};
         $status_info_text.=_T("\tPackage\t\t\t\t").$info->{'Package'}."\n" if $info->{'Package'};
         $status_info_text.=_T("\tPublic License\t\t").$info->{'Public License'}."\n" if $info->{'Public License'};
         $status_info_text.=_T("\tSupport Level\t\t").$info->{'Support Level'}."\n" if $info->{'Support Level'};
         $status_info_text.=_T("\tVersion Installed\t\t").$info->{'Version Installed'}."\n" if $info->{'Version Installed'};
         $status_info_text.=_T("\tVersion on CPAN\t\t").$info->{'Version on CPAN'}."\n" if $info->{'Version on CPAN'};
        $status_info_text.=_T("\tComment\t\t\t").($mod->comment || 'N/A')."\n";
        $status_info_text.=_T("\tPath On Mirror\t\t").($mod->path || 'N/A')."\n";
        $status_info_text.=_T("\tdslip\t\t\t\t").($mod->dslip || 'N/A')."\n";
        $status_info_text.=_T("\tIs Bundle?\t\t\t").($mod->is_bundle()?_T('Yes'):_T('No'))."\n";
        #third-party information
        $status_info_text.=_T("\tThird-Party?\t\t\t").($mod->is_third_party()?_T('Yes'):_T('No'))."\n";
        if ($mod->is_third_party()) {
            my $info = $self->{cpan}->module_information($mod->name);
            $status_info_text.=
                  _T("\t\tIncluded In\t\t").$info->{name}."\n".
                  _T("\t\tModule URI\t").$info->{url}."\n".
                  _T("\t\tAuthor\t\t\t").$info->{author}."\n".
                  _T("\t\tAuthor URI\t").$info->{author_url}."\n";
        }
        $info_ctrl->AppendText($status_info_text);
        $info_ctrl->ShowPosition(0);
    }
    $self->{statusBar}->SetStatusText('');
    _uShowErr;


}
sub _info_get_versions{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;

    $self->{statusBar}->SetStatusText(_T("Getting Version Info for ").$mod->name."...");
    my $versionList=Wx::Window::FindWindowByName('info_distributions');
    $versionList->Clear();

    my @versions=();
    foreach $m ($mod->distributions()){
        my $v=($m->version || 0.0) if $m;
        push(@versions,$v) unless (grep(/$v/,@versions));
    }
    @versions=sort(@versions);
    my $numInList=@versions;
    $versionList->Append($_) foreach (@versions);
    $versionList->SetValue($versions[-1]);
    #$versionList->SetFirstItem($numInList);

    _uShowErr;
}

#get installer status info
#TODO Make this work! Store status info after build into file
#Update the status tab
sub _info_get_status{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;
    Wx::LogMessage _T("Getting status for ").$mod->name."...";

    #get status from module
    my $status=$mod->status();

    #if we haven't retrieved the file and the stored info exists
    #then use the stored values
    my $statFile=_uGetPath($self->{config},'cpp_stat_file');
    if (!defined($status->fetch) && -e $statFile && (my $Allstatus=retrieve($statFile)) ){
        $thisStat=$Allstatus->{$mod->name};
        $status=$thisStat if $Allstatus->{$mod->name};
    }
    #print Dumper $status;
    Wx::Window::FindWindowByName('info_status_installed')->SetValue($status->installed || 0);
    Wx::Window::FindWindowByName('info_status_uninstall')->SetValue($status->uninstall || 0);
    Wx::Window::FindWindowByName('info_status_fetch')->SetValue($status->fetch||'n/a');
    Wx::Window::FindWindowByName('info_status_signature')->SetValue($status->signature||0);
    Wx::Window::FindWindowByName('info_status_extract')->SetValue($status->extract||'n/a');
    Wx::Window::FindWindowByName('info_status_created')->SetValue($status->created||0);
    Wx::Window::FindWindowByName('info_status_installer_type')->SetValue($status->installer_type||'n/a');
    Wx::Window::FindWindowByName('info_status_checksums')->SetValue($status->checksums || 'n/a');
    Wx::Window::FindWindowByName('info_status_checksum_value')->SetValue($status->checksum_value||'n/a');
    Wx::Window::FindWindowByName('info_status_checksum_ok')->SetValue($status->checksum_ok || 0);
    _uShowErr;

}

#get the readme file
sub _info_get_readme{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;
    my $info_readme=Wx::Window::FindWindowByName('info_readme');
    $info_readme->Clear();
    $info_readme->AppendText(($mod->readme || 'No README Found! Check Log for more information.'));
    $info_readme->ShowPosition(0);
    _uShowErr;
}

sub _info_get_contents{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;
    my $info_contents=Wx::Window::FindWindowByName('info_contents');
    $info_contents->Clear();
    my $txt='';
    foreach $m (sort {lc($a->name) cmp lc($b->name)} $mod->contains() ){
        $txt.=$m->name."\n";
    }
    $info_contents->AppendText($txt);
    $info_contents->ShowPosition(0); #set visible position to beginning
    _uShowErr;
}
sub _info_get_validate{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;
    my $display=Wx::Window::FindWindowByName('info_validate');
    $display->Clear();
    my $txt='';
    foreach $file (sort($mod->validate) ){
        $txt.=$file."\n";
    }
    $display->AppendText( ($txt || _T("No Missing Files or No Information. See Log.")) );
    $display->ShowPosition(0); #set visible position to beginning
    _uShowErr;
}


sub _info_get_report_all{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;
    my $info_report=Wx::Window::FindWindowByName('info_report');
    $info_report->DeleteAllItems();

    #set up the listctrl
    unless ($info_report->GetColumnCount == 3){
        while ($info_report->GetColumnCount){
            $info_report->DeleteColumn(0);
        }
        $info_report->InsertColumn( 0, _T('Distribution'));
        $info_report->InsertColumn( 1, _T('Platform') );
        $info_report->InsertColumn( 2, _T('Grade') );
    }
    my @versions=$mod->fetch_report(all_versions => 1, verbose => 1);
    @versions=reverse(sort { lc($a->{platform}) cmp lc($b->{platform})} @versions);
#    print Dumper $versions[0];
    foreach $item (@versions ){
        $info_report->InsertStringItem( 0, $item->{'dist'} );
        $info_report->SetItem( 0, 1, $item->{'platform'} );
        $info_report->SetItem( 0, 2, $item->{'grade'} );
    }
    $info_report->SetColumnWidth(0,wxLIST_AUTOSIZE);
    $info_report->SetColumnWidth(1,wxLIST_AUTOSIZE);
    $info_report->SetColumnWidth(2,wxLIST_AUTOSIZE);
    _uShowErr;
}
sub _info_get_report_this{
    my $self=shift;
    my $mod=shift||$self->{thisMod};
    return unless $mod;
    my $info_report=Wx::Window::FindWindowByName('info_report');
    $info_report->DeleteAllItems();

    #set up the listctrl
    unless ($info_report->GetColumnCount == 3){
        while ($info_report->GetColumnCount){
            $info_report->DeleteColumn(0);
        }
        $info_report->InsertColumn( 0, _T('Distribution') );
        $info_report->InsertColumn( 1, _T('Platform') );
        $info_report->InsertColumn( 2, _T('Grade') );
    }

    my @versions=$mod->fetch_report(all_versions => 0, verbose => 1);
    @versions=reverse(sort { lc($a->{platform}) cmp lc($b->{platform})} @versions);
    foreach $item (@versions ){
        $info_report->InsertStringItem( 0, $item->{'dist'} );
        $info_report->SetItem( 0, 1, $item->{'platform'} );
        $info_report->SetItem( 0, 2, $item->{'grade'} );
    }
    $info_report->SetColumnWidth(0,wxLIST_AUTOSIZE);
    $info_report->SetColumnWidth(1,wxLIST_AUTOSIZE);
    $info_report->SetColumnWidth(2,wxLIST_AUTOSIZE);
    _uShowErr;
}

sub GetInfo{
    my ($menu,$cmd_event,$modName)=@_;
    my $modtree=$self->{mod_tree};

    $modtree->_get_more_info($modtree->_get_mod($modName));
}

1;