############################################################################
# Copyright (C) 2005 by Fabio Marzocca #
# thesaltydog@gmail.com #
#
# #
# This program is free software; you can redistribute it and or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the #
# Free Software Foundation, Inc., #
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
#
#
#
############################################################################
package gtkorphan_ops ;
use strict ;
use Encode qw(decode encode);
use vars qw($application);
use Glib qw(TRUE FALSE);
use Gtk2::SimpleList;
#orphaned SimpleList
use constant TOGGLE_COL =>0;
use constant SHOW_TEXT_COL =>1;
use constant H_NAME_COL =>2;
#regular treeview
use constant TV_COL_NAME => 0;
use constant TV_COL_SECT => 1;
use constant TV_COL_PRI => 2;
use constant TV_COL_SIZE =>3;
use constant TV_H_COL_NAME=>4;
my @GuessList =(
"",
"perl",
"python",
"pike",
"ruby",
"interpreters",
"section",
"dev",
"debug",
"common",
"data",
"doc",
"dummy",
"ALL"
);
my $GO_total_orf_found =0;
my $GO_total_orf_size;
my $GO_total_reg_found;
#################################
# Initialize the global variable
#################################
sub init {
my $app = shift ;
$application = $app;
}
###########################################
sub message
{
my ($messaggio,$icon)= @_;
my $dialog = Gtk2::MessageDialog->new (
$application->window,
[qw/modal destroy-with-parent/],
$icon,
'GTK_BUTTONS_OK',
$messaggio);
$dialog->set_markup($messaggio);
if ('ok' eq $dialog->run) {$dialog -> destroy;}
else {;}
$dialog->destroy;
}
###########################################
# show the right statusbar for current page
###########################################
sub show_statusbar_on_page
{
my $page = shift;
$application->statusbar->pop(0);
if ($page == 0) { #orphaned list
if ($GO_total_orf_found ==0) {
$application->statusbar->push(0,_("No orphaned libraries found."));
}
else {
$application->statusbar->push(0,_("Orphaned packages found:").
" $GO_total_orf_found ".
_("for a total of:").
" $GO_total_orf_size KB");
}
}
if ($page ==1) { #regular treeview
$application->statusbar->push(0,
_("Total regular packages found:").
" $GO_total_reg_found");
}
}
###############################
# create orphaned treeview
###############################
sub create_orphaned_treeview
{
my $treeview = shift;
my $slist = Gtk2::SimpleList->new_from_treeview ($treeview,
"" => 'bool',
"" => 'markup',
"" => 'hidden',
);
$slist->set_headers_visible(FALSE);
$slist->set_rules_hint (TRUE);
$slist->signal_connect(button_press_event => sub{
my ($widget, $event) = @_;
my ($path, $column) = $widget->get_path_at_pos ($event->coords);
return TRUE if (!defined($path));
$widget->get_selection->select_path ($path);
if ($event->button==3) { #tasto destro
popupmenu_orphaned($path,$event);
}
return FALSE;
});
my $label=$gtkorphan_app::gladexml->get_widget('label_orphans');
$label->set_markup("".
_("Select the packages that you wish to remove:").
"");
return $slist;
}
##########################################
# Find orphaned packages and populate the list
##########################################
sub
find_orphaned
{
my ($line, $line1, $line2);
my ($size,$section,$name,$priority, $mdl, $iter,$check_column);
#hour-glass
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("watch"));
Gtk2->main_iteration while (Gtk2->events_pending);
#get the command options
my $options = shift;
#clear the current list
splice(@{$application->list_orphaned->{data}});
$application->list_orphaned->set_sensitive(TRUE);
$check_column = $application->list_orphaned->get_column(TOGGLE_COL);
$check_column->set("visible",TRUE);
#build and spawn command line
my $command = $application->dorph." -Psz ".$options;
my @libraries = `$command`;
$GO_total_orf_found = 0;
$GO_total_orf_size = 0;
foreach $line (@libraries)
{
#remove cr
chomp $line;
#remove leading spaces
$line =~s/^ *//;
#split the line
($size,$section,$name,$priority) = split (/\s+/,$line);
$line1 = "".$name."";
$line2 = "\n".
_("Size:")." ".$size."KB - ".
_("Section:")." ".$section." - ".
_("Priority:")." ".$priority.
"";
push @{$application->list_orphaned->{data}}, [FALSE,$line1.$line2,$name];
$GO_total_orf_found++;
$GO_total_orf_size += $size;
}
show_statusbar_on_page($application->notebook->get_current_page());
if ($GO_total_orf_found ==0) {
$mdl = $application->list_orphaned->get_model();
$iter = $mdl->append;
$mdl->set($iter,
SHOW_TEXT_COL,"\n *** "._("No orphaned libraries found.")."... ***");
$check_column->set("visible",FALSE);
}
#restore pointer
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("left-ptr"));
}
sub get_orphan_options
{
my $options="";
my ($find_config,$guess_combo,$guess_id,$find_all );
$find_config=$gtkorphan_app::gladexml->get_widget('find_config');
$guess_combo=$gtkorphan_app::gladexml->get_widget('guess');
$find_all = $gtkorphan_app::gladexml->get_widget('find_all');
#find-config checked?
$options .= " --find-config" if ($find_config->get_active()) ;
#all packages checked?
$options .= " --all-packages" if ($find_all->get_active()) ;
#GUESS options
$guess_id = $guess_combo->get_active();
if ($guess_id != 0 && $guess_id != 13) {
$options .= " --guess-".$GuessList[$guess_id];
}
elsif ($guess_id == 13) {
$options .= " --guess-all";
}
return $options;
}
sub invalidate_orphaned {
$application->list_orphaned->set_sensitive(FALSE) if $GO_total_orf_found >0;
}
####################################
# Remove packages
#####################################
sub remove_packages()
{
my ($line, $n_packages);
my @Removing_Packages;
foreach $line (@{$application->list_orphaned->{data}}) {
if ($line->[TOGGLE_COL]) {
push @Removing_Packages,$line->[H_NAME_COL];
}
}
$n_packages = @Removing_Packages;
return -1 if ($n_packages==0);
my $retcode = (last_check(\@Removing_Packages));
progress_removing(\@Removing_Packages) if ($retcode==TRUE);
return $retcode;
}
sub progress_removing
{
my $rem_pack=shift;
my $PID;
my $ok_btn;
my $progress_dlg = $gtkorphan_app::gladexml->get_widget('progress_dlg');
my $label =$gtkorphan_app::gladexml->get_widget('progress_label');
my $textarea =$gtkorphan_app::gladexml->get_widget('progress_text');
$rem_pack = join(" ",@$rem_pack);
$label->set_markup("".
_("Removing requested packages...").
"");
$ok_btn=$gtkorphan_app::gladexml->get_widget('okbutton3');
my $textbuffer = Gtk2::TextBuffer->new();
$textarea->set_buffer($textbuffer);
$progress_dlg->set_transient_for($application->window);
$progress_dlg->show;
$progress_dlg->signal_connect (response => sub {
if ($_[1] eq 'ok'){
$_[0]->hide;
return;}
if ($_[1] eq 'cancel') {
$ok_btn->set_sensitive(TRUE);
kill TERM => $PID;
kill KILL => $PID if kill 0 => $PID;
wait if kill 0 => $PID;
}
});
$ok_btn->set_sensitive(FALSE);
my $command = "dpkg --purge $rem_pack";
$textbuffer->insert_at_cursor(_("Starting...")."\n");
Gtk2->main_iteration while ( Gtk2->events_pending );
$PID = open(IN, "$command 2>&1 |") or die "Unable to open pipe: $!\n";
while (my $line = ) {
Gtk2->main_iteration while ( Gtk2->events_pending );
$line= decode("utf-8", $line);
$textbuffer->insert_at_cursor($line);
$textarea->scroll_to_mark($textbuffer->get_insert(),0.0,TRUE,0.0,1.0);
Gtk2->main_iteration while ( Gtk2->events_pending );
}
close(IN);
$textbuffer->insert_at_cursor(_("Done.")."\n");
$textarea->scroll_to_mark($textbuffer->get_insert(),0.0,TRUE,0.0,1.0);
$ok_btn->set_sensitive(TRUE);
}
sub last_check
{
my $rem_pack=shift;
my $pack_string=join("\n",@$rem_pack);
my $label1=$gtkorphan_app::gladexml->get_widget('label1_pending');
my $label2=$gtkorphan_app::gladexml->get_widget('label2_pending');
my $textarea=$gtkorphan_app::gladexml->get_widget('text_view_pending');
$label1->set_markup("".
_("You have requested to remove following packages from your system:").
"");
$label2->set_markup(""._("Are you sure?")."");
my $textbuffer = Gtk2::TextBuffer->new();
$textbuffer->set_text($pack_string);
$textarea->set_buffer($textbuffer);
$application->last_chance_dlg->set_transient_for($application->window);
if ('yes' eq $application->last_chance_dlg->run) {
$application->last_chance_dlg -> hide();
return TRUE;
}
else {;}
$application->last_chance_dlg->hide();
return FALSE;
}
sub popupmenu_orphaned {
my ($path,$event)=@_;
my ($selected_pack,$iter,$mdl,$pmenu);
return TRUE if ($GO_total_orf_found == 0);
$mdl = ($application->list_orphaned)->get_model();
$iter = $mdl->get_iter($path);
$selected_pack = $mdl->get($iter, H_NAME_COL);
$pmenu = Gtk2::Menu->new;
my $m_details= Gtk2::MenuItem->new (_("Show package details"));
$pmenu->add($m_details);
$m_details->show();
my $m_hiber = Gtk2::MenuItem->new (_("Hibernate package"));
$pmenu->add($m_hiber);
$m_hiber->show();
my $m_show_hiber = Gtk2::MenuItem->new (_("Show hibernated packages"));
$pmenu->add($m_show_hiber);
$m_show_hiber->show();
my $m_separator = Gtk2::MenuItem->new ();
$pmenu->append ($m_separator);
$m_separator->show;
my $m_rem = Gtk2::MenuItem->new (_("Select for removal"));
$pmenu->add($m_rem);
$m_rem->show();
my $m_selall = Gtk2::MenuItem->new (_("Select all"));
$pmenu->add($m_selall);
$m_selall->show();
my $m_unselall = Gtk2::MenuItem->new (_("Unselect all"));
$pmenu->add($m_unselall);
$m_unselall->show();
$m_details->signal_connect(activate=>sub
{show_pack_details($selected_pack);});
$m_rem->signal_connect(activate=>sub
{
$mdl->set($iter,TOGGLE_COL,TRUE);});
$m_unselall->signal_connect(activate=>sub
{$mdl->foreach(\&unselect_all,undef);});
$m_selall->signal_connect(activate=>sub
{$mdl->foreach(\&select_all,undef);});
$m_hiber->signal_connect(activate=>sub
{
my $command = $application->dorph." -A ".$selected_pack;
`$command`;
my $options = gtkorphan_ops::get_orphan_options();
gtkorphan_ops::find_orphaned($options);
});
$m_show_hiber->signal_connect(activate=>sub
{
show_hibernated_packages();
});
$pmenu->popup (undef,undef,undef,undef, $event->button, $event->time);
return TRUE;
}
sub show_hibernated_packages
{
my $hiber_dlg = $gtkorphan_app::gladexml->get_widget('hiber_dlg');
my $label =$gtkorphan_app::gladexml->get_widget('hiber_label');
my $selection =$gtkorphan_app::gladexml->get_widget('hiber_list');
my $rem_btn =$gtkorphan_app::gladexml->get_widget('rem_btn');
$label->set_markup(
_("This list shows all packages in 'hibernation' status.")."\n".
_("They will never be reported as orphaned packages while they belong to this list.")
);
my $slist = Gtk2::SimpleList->new_from_treeview ($selection,
"" => 'markup',
);
$slist->get_selection->set_mode ('multiple');
$rem_btn->set_sensitive(FALSE);
#connect on the list the button press event
$slist->signal_connect(button_press_event => sub{
my ($widget, $event) = @_;
my ($path, $column) = $widget->get_path_at_pos ($event->coords);
return TRUE if (!defined($path));
$rem_btn->set_sensitive(TRUE);
return FALSE;
});
$hiber_dlg->set_transient_for($application->window);
$hiber_dlg->show;
#dialog's segnals
$hiber_dlg->signal_connect (response => sub {
if ($_[1] eq 'ok'){
$_[0]->hide;
return;}
if ($_[1] eq 'apply') {
my @selected;
@selected=$slist->get_selected_indices;
if (scalar(@selected) > 0) {
my $packlist="";
foreach my $ind (@selected) {
$packlist .= $slist->{data}[$ind][0]." ";
}
my $cmd = $application->dorph." --del-keep ".$packlist;
`$cmd`;
}
}
if($_[1] eq 'reject') {
my $cmd = $application->dorph." -Z";
`$cmd`;
}
fill_list_keep($slist);
my $options = get_orphan_options();
find_orphaned($options);
$rem_btn->set_sensitive(FALSE) if (scalar(@{ $slist->{data} }) ==0);
});
fill_list_keep($slist);
}
sub fill_list_keep
{
my $slist = shift;
splice(@{$slist->{data}});
#prepare command
my $command = $application->dorph." --list-keep";
my @list;
@list = `$command`;
chomp (@list);
push @{$slist->{data}}, @list;
}
sub select_all
{
my ($mdl,$path,$iter)=@_;
$mdl->set($iter,TOGGLE_COL,TRUE);
return FALSE;
}
sub unselect_all
{
my ($mdl,$path,$iter)=@_;
$mdl->set($iter,TOGGLE_COL,FALSE);
return FALSE;
}
#########################################
# Regular Packages Treeview funtions
#########################################
sub create_regular_treeview
{
my $treeview = shift;
my $model;
#first column. Package name.
$treeview->insert_column_with_attributes(
TV_COL_NAME,"",
Gtk2::CellRendererText->new,
markup => TV_COL_NAME);
#second column. Package section.
$treeview->insert_column_with_attributes(
TV_COL_SECT,"",
Gtk2::CellRendererText->new,
markup => TV_COL_SECT);
#3rd column. Package priority.
$treeview->insert_column_with_attributes(
TV_COL_PRI,"",
Gtk2::CellRendererText->new,
markup => TV_COL_PRI);
#4th column. Package size.
$treeview->insert_column_with_attributes(
TV_COL_SIZE,"",
Gtk2::CellRendererText->new,
markup => TV_COL_SIZE);
$treeview->signal_connect(button_press_event => sub{
my ($widget, $event) = @_;
my ($path, $column) = $widget->get_path_at_pos ($event->coords);
return FALSE if (!defined($path));
$widget->get_selection->select_path ($path);
if ($event->button==3) { #tasto destro
gtkorphan_ops::popupmenu_regular($path,$event);
}
return FALSE;
});
$model = create_regular_model();
$treeview->set_model($model);
return $model;
}
sub create_regular_model
{
my $model = Gtk2::TreeStore->new(
"Glib::String", #TV_COL_NAME
"Glib::String", #TV_COL_SECT
"Glib::String", #TV_COL_PRI
"Glib::String", #TV_COL_SIZE
"Glib::String", #TV_H_COL_NAME
);
return $model;
}
sub populate_regular
{
my ($line, $options,$mdl, $iter, $iter1, $command);
$GO_total_reg_found=0;
#hour-glass
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("watch"));
Gtk2->main_iteration while (Gtk2->events_pending);
#get the command options
$options = shift;
#clear the current list
$mdl = $application->model_regular;
$mdl->clear();
$application->tv_regular->set_sensitive(TRUE);
#get orphaned to remove from list
#(deborphan bug on -d option
$command = $application->dorph;
my @orphaned = `$command`;
chomp (@orphaned);
my $orphaned = join(":",@orphaned);
$orphaned=$orphaned.":";
#build and spawn command line
$command = $application->dorph." -Psz -d".$options;
my @libraries = `$command`;
chomp (@libraries);
foreach $line (@libraries)
{
if (ord($line) != 32) { #checks if first char is a space 0x20
my ($name,$sect,$pri,$size)=$line =~ /(\w+.*?)\s+\((\w+.*?)\s+\-\s+(\w+),\s+(\d+)/;
next if (index($orphaned,$name)!=-1); #removing orphaned (deborphan bug)
$iter1 = $mdl->append(undef);
$iter = $iter1;
my $boldname = "$name";
$mdl->set($iter,
TV_COL_NAME,$boldname,
TV_COL_SECT,$sect,
TV_COL_PRI,$pri,
TV_COL_SIZE,$size." KB",
TV_H_COL_NAME,$name
);
$GO_total_reg_found++;
}
else {
$line =~s/^ *//; #remove leading spaces
my $italic_line = "$line";
$iter=$mdl->append($iter1);
$mdl->set($iter,TV_COL_NAME,$italic_line,TV_H_COL_NAME,$line);
}
}
show_statusbar_on_page($application->notebook->get_current_page());
#restore pointer
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("left-ptr"));
}
sub invalidate_regular {
$application->tv_regular->set_sensitive(FALSE);
}
sub get_regular_options {
my $check_all;
my $options="";
$check_all=$gtkorphan_app::gladexml->get_widget('check_all_regular');
#check_all checked?
$options .= " -a" if ($check_all->get_active()) ;
return $options;
}
sub alfa_sort {
my ($col_id,$mdl);
my ($order,$page) = @_;
$page = $application->notebook->get_current_page()
if (!defined($page));
#hour-glass
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("watch"));
Gtk2->main_iteration while (Gtk2->events_pending);
if ($page==0) {
$mdl= ($application->list_orphaned)->get_model();
$col_id =H_NAME_COL;
}
if ($page==1) {
$mdl = $application->model_regular;
$col_id= TV_COL_NAME;
}
$mdl->set_sort_column_id($col_id, $order);
#restore pointer
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("left-ptr"));
}
sub popupmenu_regular {
my ($path,$event)=@_;
my ($selected_pack,$iter,$mdl,$pmenu);
$mdl = $application->model_regular;
$iter = $mdl->get_iter($path);
$selected_pack = $mdl->get($iter, TV_H_COL_NAME);
$pmenu = Gtk2::Menu->new;
my $details= Gtk2::MenuItem->new (_("Show package details"));
$details->signal_connect(activate=>sub
{show_pack_details($selected_pack);});
$pmenu->add($details);
$details->show();
$pmenu->popup (undef,undef,undef,undef, $event->button, $event->time);
return TRUE;
}
sub show_pack_details
{
my $pack = shift;
return if ($GO_total_orf_found ==0);
my $command = "dpkg -s $pack";
my $label=$gtkorphan_app::gladexml->get_widget('details_label');
my $textarea=$gtkorphan_app::gladexml->get_widget('details_text');
$label->set_markup("".
_("Showing details informations for package:").
"\n\n$pack");
#hour-glass
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("watch"));
Gtk2->main_iteration while (Gtk2->events_pending);
#run command
my @text_array = `$command`;
my $textbuffer = Gtk2::TextBuffer->new();
#utf-8 stuff
my $string= decode("utf-8", join("",@text_array));
$textbuffer->set_text($string);
$textarea->set_buffer($textbuffer);
#restore pointer
$application->window -> window -> set_cursor(Gtk2::Gdk::Cursor -> new("left-ptr"));
$application->details_dlg->set_transient_for($application->window);
$application->details_dlg->run;
$application->details_dlg->hide;
}
1;