############################################################################ # 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;