#!/usr/bin/perl
# Copyright Jean-Philippe Guillemin <jp.guillemin@free.fr>. 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. Please take a look at http://www.gnu.org/copyleft/gpl.htm
#
# Netpkg is a tool for easily install or upgrade packages via the network. With Netpkg,
# you can make a minimal installation of Zenwalk Linux and install/upgrade just the
# packages you need most.
#
#


use strict;
use File::Path;
use Getopt::Std;
use POSIX;
use File::Find ();
use ZW::Netpkg::Netpkg;
use ZW::Netpkg::PVfilter;
use HTML::Entities qw(encode_entities_numeric);
use Locale::gettext;
use Time::Local;
use Encode;
use Unicode::Normalize; #for accent-insensitive sort

use Gtk2 '-init' ;
use Glib;
set_locale Gtk2;  # internationalize

setpriority(0, $$, 10);

my $version="4.6";

#set debug to 1 to enable
my $debug = 0;

# Threads support #####################################

use threads ('yield',
             'stack_size' => 64*4096,
             'exit' => 'threads_only',
             'stringify');
use threads::shared;
my $semaphorus : shared = 0;
our $GEvent;

####################################################################
############################ CONSTANTS  ############################
####################################################################

use constant CATEGORY => 0;
use constant AVAILABLE_PKG => 1;
use constant SORTSTRING => 2;
use constant SIZE_COMPRESSED => 3;
use constant SIZE_UNCOMPRESSED => 4;
use constant DEPENDENCIES => 5;
use constant AVAILABLE_VERSION => 6;
use constant AVAILABLE_BUILD => 7;
use constant DESCRIPTION => 8;
use constant INSTALLED_PKG => 9;
use constant INSTALLED_VERSION => 10;
use constant INSTALLED_BUILD => 11;
use constant STATUS => 12;
use constant SELECTED_AS_PKG => 13;
use constant SELECTED_AS_DEP => 14;
use constant BLACKLISTED => 15;
use constant PATH => 16;
use constant USED_BY => 17;
use constant INSTALLED_DEPENDENCIES => 18;
use constant MISSING_DEPENDENCIES => 19;
use constant CATEGORY_ICON => 20;
use constant LOCAL_PKG => 21;
use constant BUFFER => 30;

use constant STATUS_OK => 0;
use constant STATUS_DOWNGRADED => -1;
use constant STATUS_UPDATED => 1;
use constant STATUS_NEW => 2;
use constant STATUS_UNAVAILABLE => -2;

use constant PROCESS_PKGS_ONLY => 1;
use constant PROCESS_PKGS_AND_DEPS => 2;

use constant NONE => 0b00000000;
use constant ALL => 0b00001111;
use constant NEW => 0b00001000;
use constant NOT_NEW => 0b00000111;
use constant UPDATED => 0b00000100;
use constant NOT_UPDATED => 0b00001011;
use constant INSTALLED => 0b00000010;
use constant NOT_INSTALLED => 0b00001101;
use constant DOWNGRADED => 0b00000001;
use constant NOT_DOWNGRADED => 0b00001110;
# MODIFIED = UPDATED | DOWNGRADED
use constant MODIFIED => 0b00000101;
use constant NOT_MODIFIED => 0b00001010;
use constant ORPHANS => 0b00010000;
use constant NOT_ORPHANS => 0b00001111;

use constant ICON_COLUMN => 0;
use constant SOFT_COLUMN => 1;
use constant AVAILABLE_PKG_COLUMN => 2;
use constant INSTALLED_PKG_COLUMN => 3;
use constant SELECTED_PKG_COLUMN => 4;

my $ProcessDotNew;




####################################################################
############################ SESSION HANDLING ######################
####################################################################

my $user=$ENV{USER};
my $home=$ENV{HOME};
my $uid=getuid();
my $lockfile="/var/run/netpkg.pid";

$SIG{'INT'} = sub { &MainExit };
$SIG{'TERM'} = sub { &MainExit };
$SIG{'KILL'} = sub { &MainExit };
$SIG{'QUIT'} = sub { &MainExit };

if ( $uid ne 0 ) {
	print "Please launch me as root\n";
	exit;
}elsif( -e "$lockfile" ) {
	print "Another Netpkg seems to be running\n";
	exit;
}else{
	open(FH, ">$lockfile");
	print FH "$$";
	close(FH);
	chmod '0660', "$lockfile";
}


####################################################################
############################ CONFIG ###############################
####################################################################

# Internationalization
setlocale(LC_ALL, "");
textdomain("netpkg");

# Retrieve configuration
my $configfile="/etc/netpkg.conf";

my %config = ( 	'Local_repository' => '/var/packages',
				'Package_logs' => '/var/log/packages',
				'Black_list' => 'aaa_base',
				'Keep_packages' => 'yes',
				'Protected_files' => '',
				'Netpkg_dir' => '/var/netpkg',
				'Handle_dependencies' => 'yes',
				'Proxy_Socket' => '',
				'Proxy_User' => '',
				'Proxy_Password' => '',
				'Logfile' => '/var/log/netpkg.log' );

my $Netpkg = Netpkg->new();

my %db = [];
my $Data = "";

my $option;
for $option ( keys %config ) {
	$config{$option} = $Netpkg->Configure($option, $config{$option}, $configfile); 
}



my %Mirrors; 
$Netpkg->GetMirrors(\%Mirrors, $configfile );

my @Protectedfiles = split ( / / , $Netpkg->{Protected_files}); 

my $ShowOnly = $Netpkg->GetFilters;
if ( $ShowOnly == -1 ) { $ShowOnly = NONE } 
my $ChecklistMode = 0;

mkpath ("$Netpkg->{Netpkg_dir}", 0, 0755);
mkpath ("$Netpkg->{Netpkg_dir}/db", 0, 0755);


####################################################################
################################ GTK ###############################
####################################################################

my $WindowIconPath = "/usr/share/icons/hicolor/scalable/apps/netpkg.svg";

# Create windows
my $MainWindow = Gtk2::Window->new('toplevel') ;
my $ActionWindow = Gtk2::Window->new('toplevel') ;

# Configure main window
$MainWindow->set_resizable(TRUE);
$MainWindow->set_default_size (650, 550);
$MainWindow->set_title("Netpkg - $version");
$MainWindow->signal_connect(destroy => \&MainExit);
$MainWindow->set_border_width(5);
$MainWindow->set_icon_from_file("$WindowIconPath");


# Tooltips ##############################

my $tooltips = Gtk2::Tooltips->new ;


# mirrors URL selector ##############################

my $UrlSelector = Gtk2::ComboBoxEntry->new_text;
$tooltips->set_tip ($UrlSelector, decode('utf8',gettext("Enter the URL to mirror or choose one from the history. Mirrors which have been loaded properly will be auto-saved in the history")), FALSE);
for $_ ( sort { "$Mirrors{$a}" cmp "$Mirrors{$b}" } keys %Mirrors ) { $UrlSelector->append_text ($_) }
$UrlSelector->set_active (0);
# $UrlSelector->signal_connect( 'changed' , \&LoadMirror, $UrlSelector ) ; 
my $Mirror = $UrlSelector->get_active_text;

my $icon1 = Gtk2::Image->new_from_icon_name ("reload", 'GTK_ICON_SIZE_MENU');
my $RefreshButton = Gtk2::Button->new();
$RefreshButton->set_image ($icon1);
$tooltips->set_tip ($RefreshButton, decode('utf8',gettext("Load or reload mirror content")), FALSE);
$RefreshButton->signal_connect(clicked => \&LoadMirror);
my $MirrorBox = Gtk2::HBox->new(FALSE,5);
$MirrorBox->pack_start($UrlSelector, TRUE, TRUE, 5);
$MirrorBox->pack_start($RefreshButton, FALSE, TRUE, 5);

# info section ##############################

my $MainInfoLabel = Gtk2::Label->new;
$MainInfoLabel->set_width_chars(80);
$MainInfoLabel->set_justify('GTK_JUSTIFY_FILL');
$MainInfoLabel->set_line_wrap (TRUE);
$MainInfoLabel->set_line_wrap_mode ('word');
$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Welcome to Netpkg !"))."</b></span>\n");
$MainInfoLabel->set_selectable(TRUE);

my  $ScrolledLabel = Gtk2::ScrolledWindow->new (undef, undef);
$ScrolledLabel->set_shadow_type ('GTK_SHADOW_NONE');
$ScrolledLabel->set_policy ('automatic', 'automatic');
$ScrolledLabel->set_size_request (550, 170);
$ScrolledLabel->add_with_viewport($MainInfoLabel);

my $InfoBox = Gtk2::HBox->new;
$InfoBox->pack_start($ScrolledLabel, TRUE, TRUE, 3);

# Tree #########################################

#create a scrolled window that will host the treeview
my $ScrolledWindow = Gtk2::ScrolledWindow->new (undef, undef);
$ScrolledWindow->set_shadow_type ('etched-out');
$ScrolledWindow->set_policy ('automatic', 'automatic');
$ScrolledWindow->set_size_request (550, 300);
$ScrolledWindow->set_border_width(5);
our $TreeStore = Gtk2::TreeStore->new('Glib::String', 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::String');

	
#This will create the main treeview, specify $TreeStore as its model
our $TreeView = Gtk2::TreeView->new($TreeStore);
my $select = $TreeView->get_selection();
$select->set_mode('single');
$select->signal_connect(changed => \&RefreshInfoLabel);
$select->unselect_all;

#create a Gtk2::TreeViewColumn to add to $TreeView
my $SoftColumn = Gtk2::TreeViewColumn->new();
$SoftColumn->set_title (decode('utf8',gettext("Package")));
$SoftColumn->set_min_width(250);
my $icon = Gtk2::CellRendererPixbuf->new;	
$SoftColumn->pack_start ($icon, 0);	
$SoftColumn->set_attributes( $icon, 'icon-name' => ICON_COLUMN );	
my $text1 = Gtk2::CellRendererText->new;
$SoftColumn->pack_start ($text1, 0);
$SoftColumn->add_attribute ($text1, text => SOFT_COLUMN );
$SoftColumn->set_clickable (TRUE);

my $AvailablePkgColumn = Gtk2::TreeViewColumn->new();
$AvailablePkgColumn->set_title (decode('utf8',gettext("Available")));
$AvailablePkgColumn->set_min_width(70);
my $text2 = Gtk2::CellRendererText->new;
$AvailablePkgColumn->pack_start ($text2, 0);
$AvailablePkgColumn->add_attribute ($text2, text => AVAILABLE_PKG_COLUMN );  

my $InstalledPkgColumn = Gtk2::TreeViewColumn->new();
$InstalledPkgColumn->set_title (decode('utf8',gettext("Installed")));
$InstalledPkgColumn->set_min_width(70);
my $text3 = Gtk2::CellRendererText->new;
$InstalledPkgColumn->pack_start ($text3, 0);
$InstalledPkgColumn->add_attribute ($text3, text => INSTALLED_PKG_COLUMN );  	

my $SelectedPkgColumn = Gtk2::TreeViewColumn->new();
$SelectedPkgColumn->set_title (decode('utf8',gettext("Select")));
$SelectedPkgColumn->set_min_width(250);
my $icon4 = Gtk2::CellRendererPixbuf->new;	
$SelectedPkgColumn->pack_start ($icon4, 0);	
$SelectedPkgColumn->set_attributes( $icon4, 'icon-name' => SELECTED_PKG_COLUMN );	


# add $columns to the treeview
$TreeView->append_column ($SoftColumn);
$TreeView->append_column ($AvailablePkgColumn);	
$TreeView->append_column ($InstalledPkgColumn);
$TreeView->append_column ($SelectedPkgColumn);		

# make a collumn searchable
$TreeView->set_search_column(1);

$TreeView->signal_connect (row_activated => \&SelectCallback);
$TreeView->signal_connect (button_press_event => \&MenuPopup);

sub CheckThePackage {
	
	my ($iter, $soft) = @_;	
	
	my $iconstatus;
	my $iconname;
	if (@{$db{$soft}}[SELECTED_AS_PKG]) {
		$iconstatus = "dialog-ok";
	}else{
		$iconstatus = "";
	}
	if (@{$db{$soft}}[BLACKLISTED]){
		$iconname = "emblem-noread";
	}else{
		if ( @{$db{$soft}}[STATUS] == STATUS_OK ) {
			$iconname = "netpkg-ins";
		}elsif ( @{$db{$soft}}[STATUS] == STATUS_DOWNGRADED ) {
			$iconname = "netpkg-down";
		}elsif ( @{$db{$soft}}[STATUS] == STATUS_UPDATED ) {
			$iconname = "netpkg-upd";
		}elsif ( @{$db{$soft}}[STATUS] == STATUS_NEW ) {
			$iconname = "package";
		}
	}
	$TreeStore->set ($iter, ICON_COLUMN, "$iconname", 
							SOFT_COLUMN, "$soft", 
							AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
							INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
							SELECTED_PKG_COLUMN, "$iconstatus");
	return 0;
}

sub SelectCallback {
	my ($TreeView, $path, $column) = @_;
	my $iter = $TreeStore->get_iter($path);
	if (defined $iter) {
		my $soft = $TreeStore->get ($iter, SOFT_COLUMN);
		if ( @{$db{$soft}}[BLACKLISTED] == 1 ) {
			return 0; 
		}	
		if ( defined @{$db{$soft}}[DESCRIPTION] ) {	
			print "$soft\n";
			@{$db{$soft}}[SELECTED_AS_PKG] = !@{$db{$soft}}[SELECTED_AS_PKG];	
			&CheckThePackage($iter, $soft);
		}
	}
	return 0;	
}


sub MenuPopup {
	my $TreeView = shift;
	my $Event = shift;
	
	if ($Event->button != 3) {  return 0 };
	my ($path, $col, $cell_x, $cell_y) = $TreeView->get_path_at_pos ($Event->x, $Event->y);
	my $iter = $TreeStore->get_iter ($path);
	my $soft = $TreeStore->get ($iter, SOFT_COLUMN);
	if ( defined @{$db{$soft}}[DESCRIPTION] ) {	
		my $menu = Gtk2::Menu->new;
		$menu->signal_connect (destroy => sub {return 0;});
		
		if ( @{$db{$soft}}[BLACKLISTED] == 0 ) {
			if ( @{$db{$soft}}[AVAILABLE_PKG] ne "" ) {	
				my $itemInstall = Gtk2::ImageMenuItem->new (decode('utf8',gettext("Install")));
				$itemInstall->set_image(Gtk2::Image->new_from_icon_name ("filesave", 'GTK_ICON_SIZE_MENU') );
				$itemInstall->signal_connect (activate => \&InstallCallback, $soft);
				$menu->append ($itemInstall);
				$itemInstall->show;
			}	
			if ( @{$db{$soft}}[INSTALLED_PKG] ne "" ) {	
				my $itemUninstall = Gtk2::ImageMenuItem->new (decode('utf8',gettext("Uninstall")));
				$itemUninstall->set_image(Gtk2::Image->new_from_icon_name ("trash-full", 'GTK_ICON_SIZE_MENU') );
				$itemUninstall->signal_connect (activate => \&UninstallCallback, $soft);
				$menu->append ($itemUninstall);
				$itemUninstall->show;
			}
			if ( ! @{$db{$soft}}[SELECTED_AS_PKG] ) {	
				my $itemSelect = Gtk2::ImageMenuItem->new (decode('utf8',gettext("Select")));
				$itemSelect->set_image(Gtk2::Image->new_from_icon_name ("dialog-ok", 'GTK_ICON_SIZE_MENU') );
				$itemSelect->signal_connect (activate => \&SelectFromMenuCallback, $soft);
				$menu->append ($itemSelect);
				$itemSelect->show;
			}else{
				my $itemSelect = Gtk2::ImageMenuItem->new (decode('utf8',gettext("Unselect")));
				$itemSelect->set_image(Gtk2::Image->new_from_icon_name ("edit-clear", 'GTK_ICON_SIZE_MENU') );
				$itemSelect->signal_connect (activate => \&SelectFromMenuCallback, $soft);
				$menu->append ($itemSelect);
				$itemSelect->show;			
			}
					
					
			my $itemBlacklist = Gtk2::ImageMenuItem->new (decode('utf8',gettext("Blacklist")));
			$itemBlacklist->set_image(Gtk2::Image->new_from_icon_name ("emblem-noread", 'GTK_ICON_SIZE_MENU') );
			$itemBlacklist->signal_connect (activate => \&BlacklistCallback, $soft);
			$menu->append ($itemBlacklist);
			$itemBlacklist->show;	

			if (opendir(DH, "$Netpkg->{Local_repository}/@{$db{$soft}}[PATH]") ){ 
				foreach (sort readdir(DH)) {
					if ( $_ =~ /\Q${soft}\E-([^-]*)-([^-]*)-([^-]*).t[glx]z\s*$/ ){
						my $version = $1;
						my $LocalPkg = $_ ;
						my $Status = &ZW::Netpkg::PVfilter::test_package("$LocalPkg", "@{$db{$soft}}[INSTALLED_PKG]");
						if ( $Status == STATUS_DOWNGRADED ) {
							my $Label = decode('utf8',gettext("Rollback to"))." $version";
							my $itemRollback = Gtk2::ImageMenuItem->new ($Label);
							$itemRollback->set_image(Gtk2::Image->new_from_icon_name ("edit-undo", 'GTK_ICON_SIZE_MENU') );
							$itemRollback->signal_connect (activate => \&RollbackCallback, $LocalPkg);
							$menu->append ($itemRollback);
							$itemRollback->show;
						}
					}
				}
				closedir(DH); 	
			}					
			
		}else{
			my $itemWhitelist = Gtk2::ImageMenuItem->new (decode('utf8',gettext("Whitelist")));
			$itemWhitelist->set_image(Gtk2::Image->new_from_icon_name ("emblem-certified", 'GTK_ICON_SIZE_MENU') );
			$itemWhitelist->signal_connect (activate => \&WhitelistCallback, $soft);
			$menu->append ($itemWhitelist);
			$itemWhitelist->show;
		}	
		
		$menu->popup (undef, undef, undef, undef, $Event->button, $Event->time); 
	}
	return 0;  
} 

sub RollbackCallback {
	
	my $soft;
	for $soft ( keys %db ) {
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
	}
	my $Package = $_[1];
	my $select = $TreeView->get_selection();
	if ( defined $select ) { 
		my $iter = $select->get_selected;
		my $soft = $TreeStore->get ($iter, SOFT_COLUMN);
		@{$db{$soft}}[LOCAL_PKG] = $Package;
		@{$db{$soft}}[SELECTED_AS_PKG] = 1;
		&DialogLocalInstall;					
	}	
}


sub InstallCallback {
	my $soft;
	for $soft ( keys %db ) {
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
	}	
	$soft = $_[1];
	@{$db{$soft}}[SELECTED_AS_PKG] = 1 ;
	my $select = $TreeView->get_selection();	
	
	if ( defined $select ) { 
		my $iter = $select->get_selected;
		&CheckThePackage($iter, $soft);
	}
	&DialogInstall;				
	return 0;	
}

sub UninstallCallback {
	my $soft;
	for $soft ( keys %db ) {
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
	}	
	$soft = $_[1];
	@{$db{$soft}}[SELECTED_AS_PKG] = 1 ;
	my $select = $TreeView->get_selection();	
	
	if ( defined $select ) { 
		my $iter = $select->get_selected;
		&CheckThePackage($iter, $soft);
	}			
	&DialogUninstall;	
	return 0;	
}	

sub SelectFromMenuCallback {
	my $soft = $_[1];
	if ( @{$db{$soft}}[BLACKLISTED] == 1 ) {
		return 0; 
	}
	@{$db{$soft}}[SELECTED_AS_PKG] = !@{$db{$soft}}[SELECTED_AS_PKG] ;
	my $select = $TreeView->get_selection();	
	if ( defined $select ) { 
		my $iter = $select->get_selected;
		&CheckThePackage($iter, $soft);
	}				
	return 0;	
}

sub BlacklistCallback {
	my $soft = $_[1];
	@{$db{$soft}}[BLACKLISTED] = 1;
	my $select = $TreeView->get_selection();
	if ( defined $select ) { 
		my $iter = $select->get_selected;
		my $iconstatus;
		if (@{$db{$soft}}[SELECTED_AS_PKG]) {
			$iconstatus = "dialog-ok";
		}else{
			$iconstatus = "";
		}

		$TreeStore->set ($iter, ICON_COLUMN, "emblem-noread", 
						SOFT_COLUMN, "$soft", 
						AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
						INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
						SELECTED_PKG_COLUMN, "$iconstatus");
	}
	&RefreshInfoLabel;
	
	$Netpkg->{Black_list} = "";
	for $soft ( keys %db ) {
		if (@{$db{$soft}}[BLACKLISTED] == 1 ) {
			$Netpkg->{Black_list} .= " $soft";
		}
	}
	$Netpkg->SaveConf("Black_list", "$Netpkg->{Black_list}", $configfile);
	return 0;
}

sub WhitelistCallback {
	my $soft = $_[1];
	my $iconname;
	@{$db{$soft}}[BLACKLISTED] = 0;
	my $select = $TreeView->get_selection();
	if ( defined $select ) { 
		my $iter = $select->get_selected;
		my $iconstatus;
		if (@{$db{$soft}}[SELECTED_AS_PKG]) {
			$iconstatus = "dialog-ok";
		}else{
			$iconstatus = "";
		}
		if ( @{$db{$soft}}[STATUS] == STATUS_OK ) {
			$iconname = "netpkg-ins";
		}elsif ( @{$db{$soft}}[STATUS] == STATUS_DOWNGRADED ) {
			$iconname = "netpkg-down";
		}elsif ( @{$db{$soft}}[STATUS] == STATUS_UPDATED ) {
			$iconname = "netpkg-upd";
		}elsif ( @{$db{$soft}}[STATUS] == STATUS_NEW ) {
			$iconname = "package";
		}
		$TreeStore->set ($iter, ICON_COLUMN, "$iconname", 
						SOFT_COLUMN, "$soft", 
						AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
						INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
						SELECTED_PKG_COLUMN, "$iconstatus");
	}
	&RefreshInfoLabel;
	
	$Netpkg->{Black_list} = "";
	for $soft ( keys %db ) {
		if (@{$db{$soft}}[BLACKLISTED] == 1 ) {
			$Netpkg->{Black_list} .= " $soft";
		}
	}
	$Netpkg->SaveConf("Black_list", "$Netpkg->{Black_list}", $configfile);
	return 0;	
	
}

# Allow sorting on the column
$SoftColumn->set_sort_column_id(0);

$ScrolledWindow->add($TreeView);
	
# The frame around the tree  
my $TreeFrame = Gtk2::Frame->new('');
$TreeFrame->set_border_width(3);
$TreeFrame->add($ScrolledWindow);

# Filters #################################

my $ShowNew = Gtk2::CheckButton->new_with_label (decode('utf8',gettext("Not installed")));
$tooltips->set_tip ($ShowNew, decode('utf8',gettext("Show new packages, which are available on the mirror and not installed yet")), FALSE);
$ShowNew->set_active(TRUE) if ( $ShowOnly & NEW );
$ShowNew->signal_connect( toggled =>\&NewCallback);

my $ShowInstalled = Gtk2::CheckButton->new_with_label (decode('utf8',gettext("Installed")));
$tooltips->set_tip ($ShowInstalled, decode('utf8',gettext("Show all installed packages")), FALSE);
$ShowInstalled->set_active(TRUE) if ( $ShowOnly & NOT_NEW && $ShowOnly != MODIFIED );
$ShowInstalled->signal_connect( toggled =>\&InstalledCallback);

my $ShowModified = Gtk2::CheckButton->new_with_label (decode('utf8',gettext("Modified")));  
$tooltips->set_tip ($ShowModified, decode('utf8',gettext("Show installed packages that have either been updated or downgraded")), FALSE);
$ShowModified->set_active(TRUE) if ( $ShowOnly == MODIFIED );	
$ShowModified->signal_connect( toggled =>\&ModifiedCallback);

my $ShowOrphans = Gtk2::CheckButton->new_with_label (decode('utf8',gettext("Orphans")));
$tooltips->set_tip ($ShowOrphans, decode('utf8',gettext("Show installed packages that are not required by any other installed package")), FALSE);
$ShowOrphans->set_active(TRUE) if ( $ShowOnly == ORPHANS );
$ShowOrphans->signal_connect( toggled =>\&OrphansCallback);

my $FilterBox = Gtk2::VBox->new;
$FilterBox->pack_start($ShowNew, TRUE, TRUE, 0);
$FilterBox->pack_start($ShowInstalled, TRUE, TRUE, 0);
$FilterBox->pack_start($ShowModified, TRUE, TRUE, 0);
$FilterBox->pack_start($ShowOrphans, TRUE, TRUE, 0);

my $FilterFrame = Gtk2::Frame->new(decode('utf8',gettext("Filters")));
$FilterFrame->set_border_width(3);
$FilterFrame->add($FilterBox);

my $PatternEntry = Gtk2::Entry->new();
$PatternEntry->set_text( "" ); 
$PatternEntry->signal_connect( 'activate' , \&FillTree, $PatternEntry ) ; 
$tooltips->set_tip ($PatternEntry, decode('utf8',gettext("Search a pattern through packages descriptions")), FALSE);


my $PatternBox = Gtk2::VBox->new(TRUE,0);
$PatternBox->pack_end($PatternEntry, TRUE, TRUE, 4);

my $icon7 = Gtk2::Image->new_from_icon_name ("search", 'GTK_ICON_SIZE_MENU');
my $PatternButton = Gtk2::Button->new();
$PatternButton->set_image ($icon7);
$tooltips->set_tip ($PatternButton, decode('utf8',gettext("Launch search")), FALSE);
$PatternButton->signal_connect(clicked => \&FillTree) ; 

my $ButtonBox = Gtk2::VBox->new(TRUE,0);
$ButtonBox->pack_end($PatternButton, FALSE, FALSE, 0);

my $SearchBox = Gtk2::HBox->new(FALSE,2);
$SearchBox->pack_start($PatternBox, FALSE, TRUE, 5);
$SearchBox->pack_end($ButtonBox, FALSE, TRUE, 5);

my $PatternFrame = Gtk2::Frame->new(decode('utf8',gettext("Search")));
$PatternFrame->set_border_width(3);
$PatternFrame->add($SearchBox);

my $TopRightBox = Gtk2::VBox->new;
$TopRightBox->pack_start($FilterFrame, FALSE, TRUE, 0);
$TopRightBox->pack_start($PatternFrame, FALSE, TRUE, 0);

# Main action button ##################
my $iconInfo = Gtk2::Image->new_from_icon_name ("contents2", 'GTK_ICON_SIZE_BUTTON');
my $ChecklistModeButton = Gtk2::ToggleButton->new();
$ChecklistModeButton->set_image ($iconInfo);
$ChecklistModeButton->set_active (FALSE);
$tooltips->set_tip ($ChecklistModeButton, decode('utf8',gettext("Checklist Mode : only show selected packages and related information")), FALSE);
$ChecklistModeButton->signal_connect(toggled => \&ChecklistModeCallback);

my $iconInstall = Gtk2::Image->new_from_icon_name ("filesave", 'GTK_ICON_SIZE_BUTTON');
my $MainInstallButton = Gtk2::Button->new();
$MainInstallButton->set_image ($iconInstall);
$tooltips->set_tip ($MainInstallButton, decode('utf8',gettext("Install button : Compute dependencies and then install/upgrade selected packages")), FALSE);
$MainInstallButton->signal_connect(clicked => \&DialogInstall);

my $iconUninstall = Gtk2::Image->new_from_icon_name ("trash-full", 'GTK_ICON_SIZE_BUTTON');
my $MainUninstallButton = Gtk2::Button->new();
$MainUninstallButton->set_image ($iconUninstall);
$tooltips->set_tip ($MainUninstallButton, decode('utf8',gettext("Uninstall button : Compute dependencies and then uninstall selected packages")), FALSE);
$MainUninstallButton->signal_connect(clicked => \&DialogUninstall);

my $ActionBox = Gtk2::HBox->new(TRUE,5);
$ActionBox->pack_start($ChecklistModeButton, TRUE, TRUE, 3);
$ActionBox->pack_start($MainInstallButton, TRUE, TRUE, 3);
$ActionBox->pack_start($MainUninstallButton, TRUE, TRUE, 3);
# Progress bar ###########################

my $ProgressBar = Gtk2::ProgressBar->new;
$ProgressBar->set_fraction(0.0);
$ProgressBar->set_text("");
$ProgressBar->set_pulse_step(0.01);

my $iconExit = Gtk2::Image->new_from_icon_name ("exit", 'GTK_ICON_SIZE_MENU');
my $ExitButton = Gtk2::Button->new();
$ExitButton->set_image ($iconExit);
$tooltips->set_tip ($ExitButton, decode('utf8',gettext("Click here to exit Netpkg!"))." \n\n".decode('utf8',gettext("Netpkg is released under the terms of the Gnu Public Licence v2,"))." Copyright Jean-Philippe Guillemin <jp.guillemin\@free.fr> \n bye :)", FALSE);
$ExitButton->signal_connect(clicked => \&MainExit) ; 

my $iconClear = Gtk2::Image->new_from_icon_name ("gtk-clear", 'GTK_ICON_SIZE_MENU');
my $CleanCacheButton = Gtk2::Button->new();
$CleanCacheButton->set_image ($iconClear);
$tooltips->set_tip ($CleanCacheButton, decode('utf8',gettext("Clean the local packages cache !")), FALSE);

$CleanCacheButton->signal_connect(clicked => \&CleanCacheCallback ) ; 

sub CleanCacheCallback {
	$CleanCacheButton->set_sensitive( FALSE );
	&Dialog (decode('utf8',gettext("Clean cache")), 
			decode('utf8',gettext("Really clean local cache ?")), 
			decode('utf8',gettext("You are about to remove all cached packages, are you sure ?")), 
			420, 
			100, 
			50,
			\&CleanCache,
			\&CheckCacheClean
			);

}

sub CheckCacheClean {
	if ( -e $Netpkg->{Local_repository} ) { $CleanCacheButton->set_sensitive( TRUE ) };
	return 0 ;
}

my $iconSearch = Gtk2::Image->new_from_icon_name ("folder-saved-search", 'GTK_ICON_SIZE_MENU');
my $DotNewButton = Gtk2::Button->new();
$DotNewButton->set_image ($iconSearch);
$tooltips->set_tip ($DotNewButton, decode('utf8',gettext("Search for .new configuration files to be activated")), FALSE);

$DotNewButton->signal_connect(clicked => \&DotNewCallback ) ; 

sub DotNewCallback {
	$DotNewButton->set_sensitive( FALSE );
	
	use vars qw/*name *dir *prune/;
	*name   = *File::Find::name;
	*dir    = *File::Find::dir;
	*prune  = *File::Find::prune;
	
	my %DotNews;
	
	File::Find::find({wanted => sub { $DotNews{"$name"} = $dir if (/^.*\.new\z/s)   } }, '/etc');	
	
	my $DotNewFile;
	my $DotNewList;
	my $NewFile;
	for $DotNewFile ( keys %DotNews ) {
		$DotNewFile =~ /^(.*)\.new$/ ;
		$NewFile = $1;
		if ( $Netpkg->{Protected_files} =~ /$NewFile/ ) { next } ;
		$DotNewList .= "$DotNewFile "
	}
	$DotNewList =~ s/ //g ;
	if ($DotNewList =~ /^ *$/ ) {$DotNewList = decode('utf8',gettext("No .new config files yet")) }
	&Dialog (decode('utf8',gettext(".new files activation")), 
			decode('utf8',gettext("Activate the following config files")), 
			$DotNewList, 
			420, 
			100, 
			50,
			\&CleanDotNew,
			\&DotNewExit,
			);
	
}

sub DotNewExit {
	$DotNewButton->set_sensitive( TRUE );
}

my $ProgressBox = Gtk2::HBox->new(FALSE,5);
$ProgressBox->pack_start($ProgressBar, TRUE, TRUE, 5);
$ProgressBox->pack_end($ExitButton, FALSE, TRUE, 5);
$ProgressBox->pack_end($DotNewButton, FALSE, TRUE, 5);
$ProgressBox->pack_end($CleanCacheButton, FALSE, TRUE, 5);
if ( ! -e $Netpkg->{Local_repository} ) { $CleanCacheButton->set_sensitive( FALSE ) };

# Packing ################################
my $RightBox = Gtk2::VBox->new(FALSE,5);
$RightBox->pack_start($TopRightBox, TRUE, TRUE, 5);
$RightBox->pack_end($ActionBox, FALSE, FALSE, 5);

my $TreeBox = Gtk2::HBox->new;
$TreeBox->pack_start($TreeFrame, TRUE, TRUE, 0);
$TreeBox->pack_start($RightBox, FALSE, FALSE, 0);

my $OuterBox = Gtk2::VBox->new;
$OuterBox->pack_start($MirrorBox, FALSE, TRUE, 0);
$OuterBox->pack_start($TreeBox, TRUE, TRUE, 0);
$OuterBox->pack_start($InfoBox, FALSE, TRUE, 0);
$OuterBox->pack_start($ProgressBox, FALSE, FALSE, 0);

$MainWindow->add($OuterBox);

# show window and enter GTK+2 loop
$MainWindow->show_all;

Gtk2->main_iteration while ( Gtk2->events_pending );

my $ref;
my $result;
$ref = $Netpkg->LoadDB(\$result, "bigdb");
%db = %$ref ;

if ( $result == 0 ) {
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Database loaded from cache, please reload the mirror before installing packages"))."</b></span>\n");
}else{
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("No database loaded, please choose a mirror and load it"))."</b></span>\n");
}

$Netpkg->CheckInstalledPkgs(\%db);

&FillTree();

Gtk2->main;

####################################################################
############################ END MAIN ##############################
####################################################################


sub MainExit {
	$Netpkg->SaveDB(\%db, "bigdb");
	unlink ("$lockfile") ;
	$Netpkg->SaveFilters($ShowOnly);
	$MainWindow->destroy;
	$ActionWindow->destroy;
	Gtk2->main_quit ;
	exit;
}


####################################################################
############################ PARSING ###############################
####################################################################

# Will download PACKAGES.TXT and parse it to generate %db
sub LoadMirror {
	my $fh;
	my ($line,$package,$soft,$desc);
	
	$RefreshButton->set_sensitive( FALSE );
	
	$Mirror = $UrlSelector->get_active_text;
	# Get and normalize $Mirror URL
	$Mirror =~ s/\/\//\//g ;
	$Mirror =~ s/^[htp:]*\/*(.*)/http:\/\/$1/ ;
	
	my $RemoteFile = $Mirror."/PACKAGES.TXT" ;

	$ProgressBar->set_fraction(0);
	$ProgressBar->set_text(decode('utf8',gettext("Connecting to mirror"))." ".$Mirror);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	my $result = $Netpkg->Get(
				$RemoteFile, 
				decode('utf8',gettext("mirror metadata")),
				1,
				NULL,
				"$Netpkg->{Netpkg_dir}/PACKAGES.TXT",
				$ProgressBar,
				0,
				0.8); 	
	
	if ( $result == 0 ) {
		for $_ ( keys %Mirrors ) { $Mirrors{$_} = 0 }
		$Mirrors{$Mirror} = 1;
		$Netpkg->SaveMirrors(\%Mirrors);
	}else{
		$ProgressBar->set_text(decode('utf8',gettext("Failed connecting to"))." ".$Mirror);		
		$ProgressBar->set_fraction(1);
		Gtk2->main_iteration while ( Gtk2->events_pending );	
		Gtk2->main_iteration while ( ! Gtk2->events_pending );	
		$ProgressBar->set_text("");
		$ProgressBar->set_fraction(0);
		Gtk2->main_iteration while ( Gtk2->events_pending );
		
		$RefreshButton->set_sensitive( TRUE );		
		return FALSE ;
	}
											
	$ProgressBar->set_text(decode('utf8',gettext("Creating database")));			
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	my $ref;
	my $WelcomeMessage;
	$ref = $Netpkg->BuildDB;
	%db = %$ref ;
	
	$ProgressBar->set_text(decode('utf8',gettext("Creating local packages database")));			
	$ProgressBar->set_fraction(0.9);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	$Netpkg->CheckInstalledPkgs(\%db);
	$Netpkg->ProcessDeps(\%db);
	$Netpkg->SaveDB(\%db, "bigdb");
	
	$ProgressBar->set_text(decode('utf8',gettext("Building package tree")));			
	$ProgressBar->set_fraction(0.95);	
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	&FillTree();
	
	$ProgressBar->set_text(decode('utf8',gettext("Done")));		
	$ProgressBar->set_fraction(1);
	Gtk2->main_iteration while ( Gtk2->events_pending );	
	Gtk2->main_iteration while ( ! Gtk2->events_pending );	
	$ProgressBar->set_text("");
	$ProgressBar->set_fraction(0);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	$RefreshButton->set_sensitive( TRUE );
	
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Database loaded"))."</b></span>\n");

}



####################################################################
# Will build / rebuild the treestore from %db data
sub FillTree {
	
	#fill the tree with %db data
	my ($previous,$soft);
	my $iter;
	my $iter_child;
	my $pattern = $PatternEntry->get_text(); 
	my $buffer; 
	my $Filter;
	my $iconname;
	$TreeStore->clear;
	if ( $ChecklistMode  == 1 ){
		$Filter = ALL;
	}else{
		$Filter = $ShowOnly;
	}
	
	for $soft ( sort { "@{$db{$a}}[SORTSTRING]" cmp "@{$db{$b}}[SORTSTRING]" } keys %db ) {

		if (defined @{$db{$soft}}[CATEGORY]) {
			chomp $soft;
			
			$buffer = $soft." ".@{$db{$soft}}[DESCRIPTION] ;
			if ( $buffer =~ /(?i)(${pattern})/ ){	
				
				if ( $ChecklistMode  == 1 && @{$db{$soft}}[SELECTED_AS_PKG] == 0 ){
					next;
				}

				if ( $previous ne "@{$db{$soft}}[CATEGORY]" ) {
					$iter = $TreeStore->append(undef);
					$TreeStore->set ($iter, ICON_COLUMN, "@{$db{$soft}}[CATEGORY_ICON]", SOFT_COLUMN, "@{$db{$soft}}[CATEGORY]", AVAILABLE_PKG_COLUMN, "", INSTALLED_PKG_COLUMN, "", SELECTED_PKG_COLUMN, NULL);
				}
				
				if ( @{$db{$soft}}[STATUS] != STATUS_NEW && $Filter & ORPHANS && @{$db{$soft}}[USED_BY] eq "" ) {
					$iter_child = $TreeStore->append($iter);
					$iconname = "netpkg-orphan";
					if (@{$db{$soft}}[BLACKLISTED] == 1) {$iconname = "emblem-noread";};
					my $iconstatus;
					if (@{$db{$soft}}[SELECTED_AS_PKG]) {
						$iconstatus = "dialog-ok";
					}else{
						$iconstatus = "";
					}
					$TreeStore->set ($iter_child, ICON_COLUMN, "$iconname", 
												SOFT_COLUMN, "$soft", 
												AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
												INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
												SELECTED_PKG_COLUMN, "$iconstatus");					
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_OK && $Filter & INSTALLED ) {
					$iter_child = $TreeStore->append($iter);
					$iconname = "netpkg-ins";
					if (@{$db{$soft}}[BLACKLISTED] == 1) {$iconname = "emblem-noread";};
					my $iconstatus;
					if (@{$db{$soft}}[SELECTED_AS_PKG]) {
						$iconstatus = "dialog-ok";
					}else{
						$iconstatus = "";
					}					
					$TreeStore->set ($iter_child, ICON_COLUMN, "$iconname", 
												SOFT_COLUMN, "$soft", 
												AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
												INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
												SELECTED_PKG_COLUMN, "$iconstatus");
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_DOWNGRADED && $Filter & DOWNGRADED ) {
					$iter_child = $TreeStore->append($iter);
					$iconname = "netpkg-down";
					if (@{$db{$soft}}[BLACKLISTED] == 1) {$iconname = "emblem-noread";};
					my $iconstatus;
					if (@{$db{$soft}}[SELECTED_AS_PKG]) {
						$iconstatus = "dialog-ok";
					}else{
						$iconstatus = "";
					}					
					$TreeStore->set ($iter_child, ICON_COLUMN, "$iconname", 
												SOFT_COLUMN, "$soft", 
												AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
												INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
												SELECTED_PKG_COLUMN, "$iconstatus");
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_UPDATED && $Filter & UPDATED ) {
					$iter_child = $TreeStore->append($iter);
					$iconname = "netpkg-upd";
					if (@{$db{$soft}}[BLACKLISTED] == 1) {$iconname = "emblem-noread";};
					my $iconstatus;
					if (@{$db{$soft}}[SELECTED_AS_PKG]) {
						$iconstatus = "dialog-ok";
					}else{
						$iconstatus = "";
					}					
					$TreeStore->set ($iter_child, ICON_COLUMN, "$iconname", 
												SOFT_COLUMN, "$soft", 
												AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
												INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
												SELECTED_PKG_COLUMN, "$iconstatus");					
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_NEW && $Filter & NEW ) {
					$iter_child = $TreeStore->append($iter);
					$iconname = "package";
					if (@{$db{$soft}}[BLACKLISTED] == 1) {$iconname = "emblem-noread";};
					my $iconstatus;
					if (@{$db{$soft}}[SELECTED_AS_PKG]) {
						$iconstatus = "dialog-ok";
					}else{
						$iconstatus = "";
					}					
					$TreeStore->set ($iter_child, ICON_COLUMN, "$iconname", 
													SOFT_COLUMN, "$soft", 
													AVAILABLE_PKG_COLUMN, "@{$db{$soft}}[AVAILABLE_VERSION]", 
													INSTALLED_PKG_COLUMN, "@{$db{$soft}}[INSTALLED_VERSION]", 
													SELECTED_PKG_COLUMN, "$iconstatus");
				}
				$previous = @{$db{$soft}}[CATEGORY];	
			}

		}
	}
	
	$TreeView->expand_all if ( $pattern ne "" );
	
}



####################################################################
############################ CALLBACKS #############################
####################################################################




####################################################################
# called when the corresponding filter is checked 
sub NewCallback { 
	if ( $ShowNew->get_active ) {  
		$ShowOrphans->set_active(FALSE);
		$ShowOnly = $ShowOnly & NOT_ORPHANS;
		$ShowModified->set_active(FALSE);
		$ShowOnly = $ShowOnly | NEW ;
	} else {  
		$ShowOnly = $ShowOnly & NOT_NEW ;
	}  

	&FillTree();
	$Netpkg->SaveFilters($ShowOnly);
	return 0 ;
}

####################################################################
# called when the corresponding filter is checked 
sub InstalledCallback {
	if ( $ShowInstalled->get_active ) { 
		$ShowOrphans->set_active(FALSE); 
		$ShowModified->set_active(FALSE); 
		$ShowOnly = $ShowOnly | NOT_NEW ;
	} else {  
		$ShowOnly = $ShowOnly & NEW ;
	}  

	&FillTree();
	$Netpkg->SaveFilters($ShowOnly);
	return 0 ;
}

####################################################################
# called when the corresponding filter is checked 
sub ModifiedCallback {
	if ( $ShowModified->get_active ) {  
		$ShowOrphans->set_active(FALSE);
		$ShowInstalled->set_active(FALSE);
		$ShowNew->set_active(FALSE);		
		$ShowOnly = MODIFIED ;
	} else {  
		$ShowOnly = $ShowOnly & NOT_MODIFIED ;
	}  
	&FillTree();
	$TreeView->expand_all;
	$Netpkg->SaveFilters($ShowOnly);
	return 0 ;
}

####################################################################
# called when the corresponding filter is checked 
sub OrphansCallback {
	if ( $ShowOrphans->get_active ) { 
		$ShowInstalled->set_active(FALSE);
		$ShowNew->set_active(FALSE);
		$ShowModified->set_active(FALSE);
		$ShowOnly = ORPHANS ;
	} else {  
		$ShowOnly = $ShowOnly & NOT_ORPHANS ;
	}  
	
	&FillTree();
	$Netpkg->SaveFilters($ShowOnly);
	return 0 ;
}



####################################################################
# called when the Checklist Mode button is toggled 
sub ChecklistModeCallback {
	if ( $ChecklistModeButton->get_active ) { 
		$FilterBox->set_sensitive( FALSE );
		
		$ChecklistMode  = 1;
		&FillTree();
		$TreeView->expand_all;
		&RefreshInfoLabel;	
	} else {  
		$ChecklistMode  = 0;
		$FilterBox->set_sensitive( TRUE );
		&FillTree();
		&RefreshInfoLabel;	
	}  
	
	return 0 ;
}


####################################################################
# called when a row is selected 
sub RefreshInfoLabel {
	
	if( $ChecklistMode  == 1 ){
		
		$Netpkg->SelectMissingDeps(\%db);
		
		my $total_download = 0;
		my $total_install = 0;

		my $primary_selection_download = 0;
		my $primary_selection_install = 0;
		
		my $dep_download = 0;
		my $dep_install = 0;
		
		# Reporting current selections and related deps
		my $soft;
		my $PkgList;
		my $DepList;	
		my $LocalList;
		my $Npkgs = 0;
		my $Ndeps = 0;
		my $Nlocal = 0;	
		for $soft ( keys %db ) {
			
			next if ( @{$db{$soft}}[BLACKLISTED] );
			
			if ( @{$db{$soft}}[STATUS] != STATUS_NEW && defined @{$db{$soft}}[INSTALLED_PKG] ) { 
				if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
					$Nlocal++;				
				}	
			}		
			if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) { 
				if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
					$Npkgs++;
					@{$db{$soft}}[SELECTED_AS_DEP] = 0;		
					$primary_selection_download += substr @{$db{$soft}}[SIZE_COMPRESSED], 0,-2;
					$primary_selection_install += substr @{$db{$soft}}[SIZE_UNCOMPRESSED], 0,-2;
					$total_download += substr @{$db{$soft}}[SIZE_COMPRESSED], 0,-2;
					$total_install += substr @{$db{$soft}}[SIZE_UNCOMPRESSED], 0,-2;
					next;
				}
				if ( @{$db{$soft}}[STATUS] != STATUS_OK && @{$db{$soft}}[SELECTED_AS_DEP]  ) {
					$Ndeps++;	
					$dep_download += substr @{$db{$soft}}[SIZE_COMPRESSED], 0,-2;
					$dep_install += substr @{$db{$soft}}[SIZE_UNCOMPRESSED], 0,-2;
					$total_download += substr @{$db{$soft}}[SIZE_COMPRESSED], 0,-2;
					$total_install += substr @{$db{$soft}}[SIZE_UNCOMPRESSED], 0,-2;		
				}
			}

		}
		
		my $PsdSizeFormat;
		my $PsiSizeFormat;
		my $DdSizeFormat;
		my $DiSizeFormat;
		my $TdSizeFormat;
		my $TiSizeFormat;
		
			if($primary_selection_download > 1024){ 
				$primary_selection_download = ceil($primary_selection_download/1024) ;
				$PsdSizeFormat = " Mo" } 
			else {
				$PsdSizeFormat = " Ko"
			}
			if($primary_selection_install > 1024){ 
				$primary_selection_install = ceil($primary_selection_install/1024) ;
				$PsiSizeFormat = " Mo" } 
			else {
				$PsiSizeFormat = " Ko"
			}
			if($dep_download > 1024){ 
				$dep_download = ceil($dep_download/1024) ;
				$DdSizeFormat = " Mo" } 
			else {
				$DdSizeFormat = " Ko"
			}
			if($dep_install > 1024){ 
				$dep_install = ceil($dep_install/1024) ;
				$DiSizeFormat = " Mo" } 
			else {
				$DiSizeFormat = " Ko"
			}
			if($total_download > 1024){ 
				$total_download = ceil($total_download/1024) ;
				$TdSizeFormat = " Mo" } 
			else {
				$TdSizeFormat = " Ko"
			}
			if($total_install > 1024){ 
				$total_install = ceil($total_install/1024) ;
				$TiSizeFormat = " Mo" } 
			else {
				$TiSizeFormat = " Ko"
			}
			
		my $PsdMarkup;
			$PsdMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Selected packages size:"))." </b></span> "."<span size=\"small\"> ".$primary_selection_download.$PsdSizeFormat."</span>\n" ;	
		my $PsiMarkup;
			$PsiMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Selected packages install size:"))." </b></span> "."<span size=\"small\"> ".$primary_selection_install.$PsdSizeFormat."</span>\n\n" ;	
		my $DdMarkup;
			$DdMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Missing dependencies packages size:"))." </b></span> "."<span size=\"small\"> ".$dep_download.$DdSizeFormat."</span>\n" ;	
		my $DiMarkup;
			$DiMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Missing dependencies packages install size:"))." </b></span> "."<span size=\"small\"> ".$dep_install.$DiSizeFormat."</span>\n\n" ;	
		my $TdMarkup;
			$TdMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Total packages size:"))." </b></span> "."<span size=\"small\"> ".$total_download.$TdSizeFormat."</span>\n" ;	
		my $TiMarkup;
			$TiMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Total packages install size:"))." </b></span> "."<span size=\"small\"> ".$total_install.$TiSizeFormat."</span>" ;	
		
		my $Markup;
		if($primary_selection_download == 0){
			$Markup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Nothing to process"))." </b></span> " ;	
		} else {
			$Markup = $PsdMarkup . $PsiMarkup . $DdMarkup . $DiMarkup . $TdMarkup . $TiMarkup ; 
		}
		$MainInfoLabel->set_markup($Markup);
		
	
	}else{
		my $iter;
		my $select = $TreeView->get_selection();
		if ( defined $select ) { 
			$iter = $select->get_selected;
		}
		if ( defined $iter ) {
			my $soft = $TreeStore->get ($iter, SOFT_COLUMN);
			if ( @{$db{$soft}}[AVAILABLE_PKG] ne "" || @{$db{$soft}}[INSTALLED_PKG] ne "" ) {
				
				# Taking care of deps 
				my $depline = 	"<span size=\"medium\"><b>".decode('utf8',gettext("Installed dependencies:"))." </b></span><span foreground=\"Blue\" size=\"small\"> ".@{$db{$soft}}[INSTALLED_DEPENDENCIES]."</span>\n".
							"<span size=\"medium\"><b>".decode('utf8',gettext("Missing or updated dependencies:"))." </b></span><span foreground=\"Red\" size=\"small\"> ".@{$db{$soft}}[MISSING_DEPENDENCIES]."</span>\n".
							"<span size=\"medium\"><b>".decode('utf8',gettext("Installed packages that require"))." $soft: </b></span><span foreground=\"DarkGreen\" size=\"small\"> ".@{$db{$soft}}[USED_BY]."</span>\n" ;
				
				my $mainline;
				if ( @{$db{$soft}}[BLACKLISTED] == 1 ) {
					print "$soft\n" if ( $debug == 1 ) ;
					$mainline = "<span foreground=\"DarkGrey\" size=\"medium\"><b>".$soft." ".decode('utf8',gettext("(blacklisted)"))."</b></span>\n";
				} else {
					if ( ! defined @{$db{$soft}}[AVAILABLE_PKG] ) {
						$mainline = "<span foreground=\"Orange\" size=\"medium\"><b>".$soft."</b></span>\n";
					}else{
						$mainline = "<span foreground=\"DarkRed\" size=\"medium\"><b>".$soft."</b></span>\n";	
					}
				}
				
				my $versionline;
				if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) {
					$versionline = "<span size=\"medium\"><b>".decode('utf8',gettext("Available version:"))." </b>"."<span foreground=\"DarkRed\" >".@{$db{$soft}}[AVAILABLE_VERSION]."</span>"." - build "."<span foreground=\"DarkRed\" >".@{$db{$soft}}[AVAILABLE_BUILD]."</span></span>";		
				}
				if ( defined @{$db{$soft}}[INSTALLED_PKG] ) {
					$versionline = $versionline."  /  <span size=\"medium\"><b>".decode('utf8',gettext("Installed version:"))." </b>"."<span foreground=\"Blue\" >".@{$db{$soft}}[INSTALLED_VERSION]."</span>"." - build "."<span foreground=\"Blue\" >".@{$db{$soft}}[INSTALLED_BUILD]."</span></span>";		
				}
				
				# We encode unsafe characters in HTML entities
				my $HTMLdesc = encode_entities_numeric(@{$db{$soft}}[DESCRIPTION]);
				
				my $Markup = 
				$mainline.$versionline.
				"\n<span size=\"medium\"><b>".decode('utf8',gettext("Description:"))." </b></span><span size=\"small\"> ".$HTMLdesc."</span>\n".
				$depline.
				"<span size=\"medium\"><b>".decode('utf8',gettext("Location:"))." </b>".@{$db{$soft}}[PATH]."</span>\n".
				"<span size=\"medium\"><b>".decode('utf8',gettext("Package size:"))." </b>".@{$db{$soft}}[SIZE_COMPRESSED]."</span>  /  ".
				"<span size=\"medium\"><b>".decode('utf8',gettext("Install size:"))." </b>".@{$db{$soft}}[SIZE_UNCOMPRESSED]."</span>\n";		
				$MainInfoLabel->set_markup($Markup);
			}else{
				$MainInfoLabel->set_markup("<span foreground=\"DarkGrey\" size=\"medium\"><b>".$soft."</b></span>\n");
			}
		}else{
			$MainInfoLabel->set_text("");
		}
	}
	
	return 0 ;
}

####################################################################
# Called when the main Install Action button is pressed
sub DialogInstall {
	
	$Netpkg->SelectMissingDeps(\%db);
	

	# Reporting current selections and related deps
	my $soft;
	my $PkgList;
	my $DepList;	
	my $LocalList;
	my $Npkgs = 0;
	my $Ndeps = 0;
	my $Nlocal = 0;	
	for $soft ( keys %db ) {
		
		next if ( @{$db{$soft}}[BLACKLISTED] );
		
		if ( @{$db{$soft}}[STATUS] != STATUS_NEW && defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
				$Nlocal++;				
				if ( $Nlocal % 2 == 0 ){
					$LocalList = $LocalList.@{$db{$soft}}[INSTALLED_PKG]."\n";
				}else{
					$LocalList = $LocalList.@{$db{$soft}}[INSTALLED_PKG]."  ";
				}	
			}	
		}		
		if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
				$Npkgs++;
				@{$db{$soft}}[SELECTED_AS_DEP] = 0;		
				if ( $Npkgs % 2 == 0 ){
					$PkgList = $PkgList.@{$db{$soft}}[AVAILABLE_PKG]."\n";
				}else{
					$PkgList = $PkgList.@{$db{$soft}}[AVAILABLE_PKG]."  ";
				}
				next;
			}
			if ( @{$db{$soft}}[STATUS] != STATUS_OK && @{$db{$soft}}[SELECTED_AS_DEP]  ) {
				$Ndeps++;	
				if ( $Ndeps % 2 == 0 ){
					$DepList = $DepList.@{$db{$soft}}[AVAILABLE_PKG]."\n";
				}else{
					$DepList = $DepList.@{$db{$soft}}[AVAILABLE_PKG]."  ";
				}		
			}
		}

	}
	
	my $PkgListMarkup;
	if ( $Npkgs == 0 ) { 
		$PkgListMarkup = "";
	}else{
		$PkgListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Available packages:"))." </b></span>\n"."<span size=\"small\"> ".$PkgList."</span>\n" ;
	}
	
	my $DepListMarkup;
	if ( $Ndeps == 0 ) { 
		$DepListMarkup = "";
	}else{
		$DepListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Missing or updated dependencies:"))." </b></span>\n"."<span size=\"small\" > ".$DepList."</span>\n" ;	
	}	

	my $LocalListMarkup;
	if ( $Nlocal == 0 ) { 
		$LocalListMarkup = "";
	}else{
		$LocalListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Installed packages:"))." </b></span>\n"."<span size=\"small\"> ".$LocalList."</span>\n" ;	
	}	
		
	if ( $Npkgs > 0 ) { 
		
		$ChecklistMode  = 1;
		&FillTree();
		$TreeView->expand_all;
		&RefreshInfoLabel;	
		
		$MainInstallButton->set_sensitive( FALSE );
		$MainUninstallButton->set_sensitive( FALSE );
		$ScrolledWindow->set_sensitive( FALSE );
		$RefreshButton->set_sensitive( FALSE );
		$CleanCacheButton->set_sensitive( FALSE );
		$FilterBox->set_sensitive( FALSE );
		
		# Configuring the window
		$ActionWindow->signal_connect(destroy => \&ActionExit);
		#$ActionWindow->set_default_size (500, 150);
		$ActionWindow->set_resizable(TRUE);
		$ActionWindow->set_title(decode('utf8',gettext("Install actions")));
		$ActionWindow->set_border_width(7);
		$ActionWindow->set_icon_from_file("$WindowIconPath");
		$ActionWindow->set_position("center");
		
		# info section
		my $InfoLabel = Gtk2::Label->new;
		$InfoLabel->set_width_chars(50);
		$InfoLabel->set_justify('GTK_JUSTIFY_FILL');
		$InfoLabel->set_line_wrap (TRUE);
		$InfoLabel->set_line_wrap_mode ('word');
		my $Markup = $PkgListMarkup . $DepListMarkup . $LocalListMarkup ;	
		$InfoLabel->set_markup($Markup);
		$InfoLabel->set_selectable(TRUE);
		$InfoLabel->can_focus(0);
				
		my $ScrollLabel = Gtk2::ScrolledWindow->new (undef, undef);
		$ScrollLabel->set_shadow_type ('etched-out');
		$ScrollLabel->set_policy ('automatic', 'automatic');
		$ScrollLabel->set_size_request (420, 150);
		$ScrollLabel->add_with_viewport($InfoLabel);

		# Actions ##################################"
		my $ExitButton = Gtk2::Button->new_from_stock('gtk-close');
		$ExitButton->signal_connect(clicked => sub{ 
														$ActionWindow->destroy; 
														$ChecklistModeButton->set_active (FALSE);
													} ) ; 
		my $icon1 = Gtk2::Image->new_from_icon_name ("gtk-save", 'GTK_ICON_SIZE_BUTTON');
		my $InstallButton = Gtk2::Button->new_with_mnemonic( decode('utf8',gettext("Install packages")));
		$InstallButton->set_image ($icon1);
		$InstallButton->signal_connect(clicked => sub{ ActionInstallPkgs(PROCESS_PKGS_ONLY) } );
		
		my $icon2 = Gtk2::Image->new_from_icon_name ("gtk-save-all", 'GTK_ICON_SIZE_BUTTON');
		my $InstallDepsButton = Gtk2::Button->new_with_mnemonic( decode('utf8',gettext("Include deps")));
		$InstallDepsButton->set_image ($icon2);
		$InstallDepsButton->signal_connect(clicked => sub{ ActionInstallPkgs(PROCESS_PKGS_AND_DEPS) } );
		
		my $CheckDotNew = Gtk2::CheckButton->new_with_label ( decode('utf8',gettext("Accept .new config files")));  
		$CheckDotNew->signal_connect( toggled =>\&CheckDotNewCallback);
		
		
		my $ActionBox = Gtk2::HBox->new;		
		if ( $Npkgs > 0 ) { $ActionBox->pack_start($InstallButton, FALSE, FALSE, 0); }
		if ( $Ndeps > 0 ) { $ActionBox->pack_start($InstallDepsButton, FALSE, FALSE, 0); }
		$ActionBox->pack_end($ExitButton, FALSE, FALSE, 0);
		if ( $Nlocal < $Npkgs ) { $ActionBox->pack_end($CheckDotNew, FALSE, FALSE, 0); }

		my $OuterBox = Gtk2::VBox->new;
		$OuterBox->pack_start($ScrollLabel, TRUE, TRUE, 0);
		$OuterBox->pack_start($ActionBox, FALSE, FALSE, 0);
		
		$ActionWindow->add($OuterBox);		
		$ActionWindow->show_all;
	}	
	
	return 0 ;
}

####################################################################
# Called when the main Install Action button is pressed
sub DialogLocalInstall {
	
	$Netpkg->SelectMissingDeps(\%db);
	

	# Reporting current selections and related deps
	my $soft;
	my $PkgList;	
	my $LocalList;
	my $Npkgs = 0;
	my $Nlocal = 0;	
	for $soft ( keys %db ) {
		
		next if ( @{$db{$soft}}[BLACKLISTED] );
		
		if ( defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
				$Nlocal++;				
				if ( $Nlocal % 2 == 0 ){
					$LocalList = $LocalList.@{$db{$soft}}[INSTALLED_PKG]."\n";
				}else{
					$LocalList = $LocalList.@{$db{$soft}}[INSTALLED_PKG]."  ";
				}	
			}	
		}		
		if ( defined @{$db{$soft}}[LOCAL_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
				$Npkgs++;
				@{$db{$soft}}[SELECTED_AS_DEP] = 0;		
				if ( $Npkgs % 2 == 0 ){
					$PkgList = $PkgList.@{$db{$soft}}[LOCAL_PKG]."\n";
				}else{
					$PkgList = $PkgList.@{$db{$soft}}[LOCAL_PKG]."  ";
				}
				next;
			}
		}

	}
	
	my $PkgListMarkup;
	if ( $Npkgs == 0 ) { 
		$PkgListMarkup = "";
	}else{
		$PkgListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Available packages:"))." </b></span>\n"."<span size=\"small\"> ".$PkgList."</span>\n" ;
	}

	my $LocalListMarkup;
	if ( $Nlocal == 0 ) { 
		$LocalListMarkup = "";
	}else{
		$LocalListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Installed packages:"))." </b></span>\n"."<span size=\"small\"> ".$LocalList."</span>\n" ;	
	}	
		
	if ( $Npkgs > 0 ) { 
		
		$ChecklistMode  = 1;
		&FillTree();
		$TreeView->expand_all;
		&RefreshInfoLabel;	
		
		$MainInstallButton->set_sensitive( FALSE );
		$MainUninstallButton->set_sensitive( FALSE );
		$ScrolledWindow->set_sensitive( FALSE );
		$RefreshButton->set_sensitive( FALSE );
		$CleanCacheButton->set_sensitive( FALSE );
		$FilterBox->set_sensitive( FALSE );
		
		# Configuring the window
		$ActionWindow->signal_connect(destroy => \&ActionExit);
		#$ActionWindow->set_default_size (500, 150);
		$ActionWindow->set_resizable(TRUE);
		$ActionWindow->set_title(decode('utf8',gettext("Install actions")));
		$ActionWindow->set_border_width(7);
		$ActionWindow->set_icon_from_file("$WindowIconPath");
		$ActionWindow->set_position("center");
		
		# info section
		my $InfoLabel = Gtk2::Label->new;
		$InfoLabel->set_width_chars(50);
		$InfoLabel->set_justify('GTK_JUSTIFY_FILL');
		$InfoLabel->set_line_wrap (TRUE);
		$InfoLabel->set_line_wrap_mode ('word');
		my $Markup = $PkgListMarkup . $LocalListMarkup ;	
		$InfoLabel->set_markup($Markup);
		$InfoLabel->set_selectable(TRUE);
		$InfoLabel->can_focus(0);
				
		my $ScrollLabel = Gtk2::ScrolledWindow->new (undef, undef);
		$ScrollLabel->set_shadow_type ('etched-out');
		$ScrollLabel->set_policy ('automatic', 'automatic');
		$ScrollLabel->set_size_request (420, 150);
		$ScrollLabel->add_with_viewport($InfoLabel);

		# Actions ##################################"
		my $ExitButton = Gtk2::Button->new_from_stock('gtk-close');
		$ExitButton->signal_connect(clicked => sub{ 
														$ActionWindow->destroy; 
														$ChecklistModeButton->set_active (FALSE);
													} ) ; 
		my $icon1 = Gtk2::Image->new_from_icon_name ("gtk-save", 'GTK_ICON_SIZE_BUTTON');
		my $InstallButton = Gtk2::Button->new_with_mnemonic( decode('utf8',gettext("Install packages")));
		$InstallButton->set_image ($icon1);
		$InstallButton->signal_connect(clicked => sub{ ActionInstallLocalPkgs(PROCESS_PKGS_ONLY) } );
		
		my $CheckDotNew = Gtk2::CheckButton->new_with_label ( decode('utf8',gettext("Accept .new config files")));  
		$CheckDotNew->signal_connect( toggled =>\&CheckDotNewCallback);
		
		my $ActionBox = Gtk2::HBox->new;		
		if ( $Npkgs > 0 ) { $ActionBox->pack_start($InstallButton, FALSE, FALSE, 0); }
		$ActionBox->pack_end($ExitButton, FALSE, FALSE, 0);
		if ( $Nlocal < $Npkgs ) { $ActionBox->pack_end($CheckDotNew, FALSE, FALSE, 0); }

		my $OuterBox = Gtk2::VBox->new;
		$OuterBox->pack_start($ScrollLabel, TRUE, TRUE, 0);
		$OuterBox->pack_start($ActionBox, FALSE, FALSE, 0);
		
		$ActionWindow->add($OuterBox);		
		$ActionWindow->show_all;
	}	
	
	return 0 ;
}

####################################################################
# Called when the main Uninstall Action button is pressed
sub DialogUninstall {
	
	$Netpkg->SelectDeps(\%db);
	
	# Reporting current selections and related deps
	my $soft;
	my $PkgList;
	my $DepList;	
	my $LocalList;
	my $WarnList;
	my $Npkgs = 0;
	my $Ndeps = 0;
	my $Nlocal = 0;	
	my $Nwarn = 0;	
	
	for $soft ( keys %db ) {
		@{$db{$soft}}[BUFFER] = @{$db{$soft}}[STATUS] ;
		
		next if ( @{$db{$soft}}[BLACKLISTED] );
		
		if ( defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] ) {
				
				@{$db{$soft}}[STATUS] = STATUS_NEW ;
				
					
				if ( @{$db{$soft}}[USED_BY] eq "" ) {	
					$Nlocal++;		
					if ( $Nlocal % 2 == 0 ){
						$LocalList = $LocalList.@{$db{$soft}}[INSTALLED_PKG]."\n";
					}else{
						$LocalList = $LocalList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}
				}else{
					$Nwarn++;
					if ( $Nwarn % 2 == 0 ){
						$WarnList = $WarnList.@{$db{$soft}}[INSTALLED_PKG]."\n";
					}else{
						$WarnList = $WarnList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}					
				}
			}		
		}
	}
	
	$Netpkg->ProcessDeps(\%db);
	
	for $soft ( keys %db ) {
		@{$db{$soft}}[STATUS] = @{$db{$soft}}[BUFFER] ;
		
		next if ( @{$db{$soft}}[BLACKLISTED] );
		
		if ( defined @{$db{$soft}}[INSTALLED_PKG] ) { 

			if ( @{$db{$soft}}[SELECTED_AS_DEP]  ) {
				if ( @{$db{$soft}}[USED_BY] eq "" ) {
					$Ndeps++;	
					if ( $Ndeps % 2 == 0 ){
						$DepList = $DepList.@{$db{$soft}}[INSTALLED_PKG]."\n";
					}else{
						$DepList = $DepList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}	
				}else{
					@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
				}
			}	
		}
	}
	
	$Netpkg->ProcessDeps(\%db);
	
	
	my $LocalListMarkup;
	if ( $Nlocal == 0 ) { 
		$LocalListMarkup = "";
	}else{
		$LocalListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Removable packages:"))." </b></span>\n"."<span size=\"small\"> ".$LocalList."</span>\n" ;	
	}	
	
	my $WarnListMarkup;
	if ( $Nwarn == 0 ) { 
		$WarnListMarkup = "";
	}else{
		$WarnListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Required packages (WARNING!):"))." </b></span>\n"."<span size=\"small\"> ".$WarnList."</span>\n" ;	
	}		
	
	my $DepListMarkup;
	if ( $Ndeps == 0 ) { 
		$DepListMarkup = "";
	}else{
		$DepListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Orphan dependencies:"))." </b></span>\n"."<span size=\"small\" > ".$DepList."</span>\n" ;	
	}	

		
	if ( $Nlocal > 0 || $Nwarn > 0 ) { 
		
		$ChecklistMode  = 1;
		&FillTree();
		$TreeView->expand_all;
		&RefreshInfoLabel;
		
		$MainInstallButton->set_sensitive( FALSE );
		$MainUninstallButton->set_sensitive( FALSE );
		$ScrolledWindow->set_sensitive( FALSE );
		$RefreshButton->set_sensitive( FALSE );
		$CleanCacheButton->set_sensitive( FALSE );
		$FilterBox->set_sensitive( FALSE );
		
		# Configuring the window
		$ActionWindow->signal_connect(destroy => \&ActionExit);
		#$ActionWindow->set_default_size (500, 150);
		$ActionWindow->set_resizable(TRUE);
		$ActionWindow->set_title(decode('utf8',gettext("Uninstall actions")));
		$ActionWindow->set_border_width(7);
		$ActionWindow->set_icon_from_file("$WindowIconPath");
		$ActionWindow->set_position("center");
		
		# info section
		my $InfoLabel = Gtk2::Label->new;
		$InfoLabel->set_width_chars(50);
		$InfoLabel->set_justify('GTK_JUSTIFY_FILL');
		$InfoLabel->set_line_wrap (TRUE);
		$InfoLabel->set_line_wrap_mode ('word');
		my $Markup = $LocalListMarkup . $WarnListMarkup . $DepListMarkup ;	
		$InfoLabel->set_markup($Markup);
		$InfoLabel->set_selectable(TRUE);
		$InfoLabel->can_focus(0);
				
		my $ScrollLabel = Gtk2::ScrolledWindow->new (undef, undef);
		$ScrollLabel->set_shadow_type ('etched-out');
		$ScrollLabel->set_policy ('automatic', 'automatic');
		$ScrollLabel->set_size_request (420, 150);
		$ScrollLabel->add_with_viewport($InfoLabel);

		# Actions ##################################"
		my $ExitButton = Gtk2::Button->new_from_stock('gtk-close');
		$ExitButton->signal_connect(clicked => sub{ $ActionWindow->destroy; 
													$ChecklistModeButton->set_active (FALSE);} ) ; 
		my $icon1 = Gtk2::Image->new_from_icon_name ("user-trash", 'GTK_ICON_SIZE_BUTTON');
		my $UninstallButton = Gtk2::Button->new_with_mnemonic( decode('utf8',gettext("Uninstall packages")));
		$UninstallButton->set_image ($icon1);
		$UninstallButton->signal_connect(clicked => sub{ ActionUninstallPkgs(PROCESS_PKGS_ONLY) } );
				
		my $icon2 = Gtk2::Image->new_from_icon_name ("edit-clear", 'GTK_ICON_SIZE_BUTTON');
		my $UninstallDepsButton = Gtk2::Button->new_with_mnemonic( decode('utf8',gettext("Include orphan deps")));
		$UninstallDepsButton->set_image ($icon2);
		$UninstallDepsButton->signal_connect(clicked => sub{ ActionUninstallPkgs(PROCESS_PKGS_AND_DEPS) } );
		
		my $CheckDotNew = Gtk2::CheckButton->new_with_label ( decode('utf8',gettext("Accept .new config files")));  
		$CheckDotNew->signal_connect( toggled =>\&CheckDotNewCallback);
		
		
		my $ActionBox = Gtk2::HBox->new;		
		if ( $Nlocal > 0 || $Nwarn > 0) { $ActionBox->pack_start($UninstallButton, FALSE, FALSE, 0); }	
		if ( $Ndeps > 0 ) { $ActionBox->pack_start($UninstallDepsButton, FALSE, FALSE, 0); }
		$ActionBox->pack_end($ExitButton, FALSE, FALSE, 0);
		if ( $Nlocal < $Npkgs ) { $ActionBox->pack_end($CheckDotNew, FALSE, FALSE, 0); }
		
		my $OuterBox = Gtk2::VBox->new;
		$OuterBox->pack_start($ScrollLabel, TRUE, TRUE, 0);
		$OuterBox->pack_start($ActionBox, FALSE, FALSE, 0);
		
		$ActionWindow->add($OuterBox);		
		$ActionWindow->show_all;
	}	
	
	return 0 ;
}

####################################################################
# Called when the .new checkbutton is checked/unchecked
sub  CheckDotNewCallback {
	my ( $checkbutton) = @_ ;
	$ProcessDotNew = $checkbutton->get_active ;
	# print "$ProcessDotNew\n";
}

####################################################################
# Called at Action window exit to enable Action button
sub ActionExit {
	$MainInstallButton->set_sensitive( TRUE );
	$MainUninstallButton->set_sensitive( TRUE );
	$ScrolledWindow->set_sensitive( TRUE );
	$RefreshButton->set_sensitive( TRUE );
	$FilterBox->set_sensitive( TRUE );
	$ChecklistMode  = 0;
	&FillTree();
	&RefreshInfoLabel;	
	&CheckCacheClean;
	return 0 ;
}

####################################################################
# Will download and install packages 
sub ActionInstallPkgs{
	
	my $ProcessType = shift;
	
	$ActionWindow->destroy;
	$TreeView->collapse_all;
	$ScrolledWindow->set_sensitive( FALSE );
	$MainInstallButton->set_sensitive( FALSE );
	$MainUninstallButton->set_sensitive( FALSE );
	$RefreshButton->set_sensitive( FALSE );
	$CleanCacheButton->set_sensitive( FALSE );
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Loading packages ..."))."</b></span>\n");
	&DownloadPkgs ($ProcessType);
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Processing packages ..."))."</b></span>\n");
	&InstallPkgs ($ProcessType);
	$Netpkg->CheckInstalledPkgs(\%db);
	$PatternEntry->set_text("");
	$ChecklistMode  = 0;
	$ChecklistModeButton->set_active( FALSE );
	&FillTree();
	&RefreshInfoLabel;	
	$MainInstallButton->set_sensitive( TRUE );
	$MainUninstallButton->set_sensitive( TRUE );	
	$ScrolledWindow->set_sensitive( TRUE );
	$RefreshButton->set_sensitive( TRUE );
	&CheckCacheClean;
	$MainInfoLabel->set_text("");
	$Netpkg->SaveDB(\%db, "bigdb");
	return 0 ;		
}

####################################################################
# Will download and install packages 
sub ActionInstallLocalPkgs{
	
	my $ProcessType = shift;
	
	$ActionWindow->destroy;
	$TreeView->collapse_all;
	$ScrolledWindow->set_sensitive( FALSE );
	$MainInstallButton->set_sensitive( FALSE );
	$MainUninstallButton->set_sensitive( FALSE );
	$RefreshButton->set_sensitive( FALSE );
	$CleanCacheButton->set_sensitive( FALSE );
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Processing packages ..."))."</b></span>\n");
	&InstallLocalPkgs ($ProcessType);
	$Netpkg->CheckInstalledPkgs(\%db);
	$PatternEntry->set_text("");
	$ChecklistMode  = 0;
	$ChecklistModeButton->set_active( FALSE );
	&FillTree();
	&RefreshInfoLabel;	
	$MainInstallButton->set_sensitive( TRUE );
	$MainUninstallButton->set_sensitive( TRUE );	
	$ScrolledWindow->set_sensitive( TRUE );
	$RefreshButton->set_sensitive( TRUE );
	&CheckCacheClean;
	$MainInfoLabel->set_text("");
	$Netpkg->SaveDB(\%db, "bigdb");
	return 0 ;		
}

####################################################################
# Will uninstall packages WITHOUT deps
sub ActionUninstallPkgs{
	
	my $ProcessType = shift;
	
	$ActionWindow->destroy;
	$TreeView->collapse_all;
	$ScrolledWindow->set_sensitive( FALSE );
	$MainInstallButton->set_sensitive( FALSE );
	$MainUninstallButton->set_sensitive( FALSE );
	$RefreshButton->set_sensitive( FALSE );
	$CleanCacheButton->set_sensitive( FALSE );
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Processing packages ..."))."</b></span>\n");
	&UninstallPkgs ($ProcessType);
	$PatternEntry->set_text("");
	$ChecklistMode  = 0;
	$ChecklistModeButton->set_active( FALSE );
	&FillTree();
	&RefreshInfoLabel;	
	$MainInstallButton->set_sensitive( TRUE );
	$MainUninstallButton->set_sensitive( TRUE );	
	$ScrolledWindow->set_sensitive( TRUE );
	$RefreshButton->set_sensitive( TRUE );
	&CheckCacheClean;
	$MainInfoLabel->set_text("");
	$Netpkg->SaveDB(\%db, "bigdb");
	return 0 ;		
}


####################################################################
# Will download packages OR deps depending on the value of $ProcessType
# DownloadPkgs($ProcessType);
sub DownloadPkgs {

	my $ProcessType = shift ;
	print "DownloadPkgs ProcessType = $ProcessType\n" if ( $debug == 1 ) ;
	
	my $Npkgs = 0 ;
	for my $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 	
				print "DownloadPkgs soft = $soft\n" if ( $debug == 1 ) ;
				$Npkgs++;				
			}
		}
	}
	print "DownloadPkgs Npkgs = $Npkgs\n" if ( $debug == 1 ) ;
	if ($Npkgs == 0) { $Npkgs = 1 }

	$ProgressBar->set_fraction(0);
	
	my $Range = 0.99 / $Npkgs  ;
	my $Progress = 0;
	$ProgressBar->set_fraction($Progress);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	my $soft;
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
				
				
				my $Path = $Netpkg->{Local_repository}."/".@{$db{$soft}}[PATH] ;
				my $File = $Netpkg->{Local_repository}."/".@{$db{$soft}}[PATH]."/".@{$db{$soft}}[AVAILABLE_PKG] ;
				
				if ( -e "$File" ) {
					$ProgressBar->set_text(decode('utf8',gettext("Skipping"))." ".@{$db{$soft}}[AVAILABLE_PKG]);
					print "Skipping @{$db{$soft}}[AVAILABLE_PKG]" if ( $debug == 1 ) ;
					Gtk2->main_iteration while ( Gtk2->events_pending );
				}else{
					my $RemoteFile = $Mirror."/".@{$db{$soft}}[PATH]."/".@{$db{$soft}}[AVAILABLE_PKG] ;
					mkpath ("$Path", 0, 0755);
					$ProgressBar->set_text(decode('utf8',gettext("Loading"))." ".@{$db{$soft}}[AVAILABLE_PKG]);
					print "Loading @{$db{$soft}}[AVAILABLE_PKG]" if ( $debug == 1 ) ;
					Gtk2->main_iteration while ( Gtk2->events_pending );
					my $buffer = "" ;
					
					my $result = $Netpkg->Get(
								$RemoteFile, 
								@{$db{$soft}}[AVAILABLE_PKG], 
								1,
								\$buffer,
								$File,
								$ProgressBar,
								$Progress,
								$Range); 	
								
					if ( ! $result == 0 ) {
						$ProgressBar->set_text(decode('utf8',gettext("Failed to download"))." ".@{$db{$soft}}[AVAILABLE_PKG]);		
						Gtk2->main_iteration while ( Gtk2->events_pending );	
						Gtk2->main_iteration while ( ! Gtk2->events_pending );	
						$Progress = $Progress + $Range ;
						delete($db{$soft});
						Gtk2->main_iteration while ( Gtk2->events_pending );
						next;
					}	
								
				}
			$Progress = $Progress + $Range ;
			}
		}
	}	
	$ProgressBar->set_fraction(1);
	$ProgressBar->set_text(decode('utf8',gettext("Done")));

	return 0 ;	
}

####################################################################
# Will install/update/reinstall packages OR deps depending on the value of $ProcessType
# InstallPkgs($ProcessType);
sub InstallPkgs {
	
	
	my $ProcessType = shift ;
	print "InstallPkgs ProcessType = $ProcessType\n" if ( $debug == 1 ) ;
	
	my $Npkgs = 0 ;
	my $soft;
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
				print "InstallPkgs soft = $soft\n" if ( $debug == 1 ) ;
				$Npkgs++;				
			}
		}
	}
	print "InstallPkgs Npkgs = $Npkgs\n" if ( $debug == 1 ) ;
	if ($Npkgs == 0) { $Npkgs = 1 }
	
	$ProgressBar->set_fraction(0);
	my $Range = 0.87 / $Npkgs  ;
	my $Progress = 0.03;
	$ProgressBar->set_fraction($Progress);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[AVAILABLE_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
			
				my $Path = $Netpkg->{Local_repository}."/".@{$db{$soft}}[PATH] ;
				my $File = $Netpkg->{Local_repository}."/".@{$db{$soft}}[PATH]."/".@{$db{$soft}}[AVAILABLE_PKG] ;
				
				if ( @{$db{$soft}}[STATUS] == STATUS_OK ) {
					$ProgressBar->set_text(decode('utf8',gettext("Reinstalling"))." ".@{$db{$soft}}[AVAILABLE_PKG]);
					$Netpkg->Reinstall( $File);				
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_DOWNGRADED ) {
					$ProgressBar->set_text(decode('utf8',gettext("Downgrading"))." ".@{$db{$soft}}[AVAILABLE_PKG]);
					$Netpkg->Update( $File);
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_UPDATED ) {
					$ProgressBar->set_text(decode('utf8',gettext("Updating"))." ".@{$db{$soft}}[AVAILABLE_PKG]);
					$Netpkg->Update( $File);
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_NEW ) {
					$ProgressBar->set_text(decode('utf8',gettext("Installing"))." ".@{$db{$soft}}[AVAILABLE_PKG]);
					$Netpkg->Install( $File);
				}
				
			$Progress = $Progress + $Range ;
			$ProgressBar->set_fraction($Progress);
			Gtk2->main_iteration while ( Gtk2->events_pending );
			}
		}
	}	
	
	$ProgressBar->set_text(decode('utf8',gettext("Refreshing database")));			
	$ProgressBar->set_fraction(0.95);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	$Netpkg->CheckInstalledPkgs(\%db);
	$Netpkg->ProcessDeps(\%db);
	
	$ProgressBar->set_fraction(1);
	$ProgressBar->set_text(decode('utf8',gettext("Done")));
	Gtk2->main_iteration while ( ! Gtk2->events_pending );	
	$ProgressBar->set_fraction(0);
	$ProgressBar->set_text("");
	
	# Check for any .new files
	if ( $ProcessDotNew ) { &CleanDotNew }
	
	
	# Checking install result !!
	my 	$BadPkgs = 0;
	my 	$GoodPkgs = 0;
	my 	$GoodList;
	my 	$BadList;
		
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			next if ( @{$db{$soft}}[BLACKLISTED] );
			
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
				if ( @{$db{$soft}}[AVAILABLE_PKG] eq @{$db{$soft}}[INSTALLED_PKG] ) {
					$GoodPkgs++;
					if ( $GoodPkgs % 2 == 0 ){
						$GoodList = $GoodList.@{$db{$soft}}[INSTALLED_PKG]."\n";
					}else{
						$GoodList = $GoodList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}
				}else{
					$BadPkgs++;	
					if ( $BadPkgs % 2 == 0 ){
						$BadList = $BadList.@{$db{$soft}}[INSTALLED_PKG]." ";
					}else{
						$BadList = $BadList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}				
				}	
			}
		}
		
		# Switching off the selection
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
	}
	
	my $GoodListMarkup;
	if ( $GoodPkgs == 0 ) { 
		$GoodListMarkup = "";
	}else{
		$GoodListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Successfully installed:"))." </b></span>\n"."<span size=\"small\"> ".$GoodList."</span>\n" ;
	}
	
	my $BadListMarkup;
	if ( $BadPkgs == 0 ) { 
		$BadListMarkup = "";
	}else{
		$BadListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Failed to install:"))." </b></span>\n"."<span size=\"small\"> ".$BadList."</span>\n" ;	
	}
	
	
	if ( $GoodPkgs > 0 || $BadPkgs > 0 ) { 
		
		&Dialog(decode('utf8',gettext("Report")), 
			decode('utf8',gettext("Install process result")),
			 $GoodListMarkup.$BadListMarkup, 
			 420, 
			 100,
			 50,
			 NULL,
			 NULL);
	}	
	
	return 0 ;	
}

####################################################################
# Will install/update/reinstall packages OR deps depending on the value of $ProcessType
# InstallPkgs($ProcessType);
sub InstallLocalPkgs {
	
	
	my $ProcessType = shift ;
	print "InstallPkgs ProcessType = $ProcessType\n" if ( $debug == 1 ) ;
	
	my $Npkgs = 0 ;
	my $soft;
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[LOCAL_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
				print "InstallPkgs soft = $soft\n" if ( $debug == 1 ) ;
				$Npkgs++;				
			}
		}
	}
	print "InstallPkgs Npkgs = $Npkgs\n" if ( $debug == 1 ) ;
	if ($Npkgs == 0) { $Npkgs = 1 }
	
	$ProgressBar->set_fraction(0);
	my $Range = 0.87 / $Npkgs  ;
	my $Progress = 0.03;
	$ProgressBar->set_fraction($Progress);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[LOCAL_PKG] ) { 
			
			@{$db{$soft}}[STATUS] = &ZW::Netpkg::PVfilter::test_package("@{$db{$soft}}[LOCAL_PKG]", "@{$db{$soft}}[INSTALLED_PKG]") ;					
			
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
			
				my $Path = $Netpkg->{Local_repository}."/".@{$db{$soft}}[PATH] ;
				my $File = $Netpkg->{Local_repository}."/".@{$db{$soft}}[PATH]."/".@{$db{$soft}}[LOCAL_PKG] ;
				
				if ( @{$db{$soft}}[STATUS] == STATUS_OK ) {
					$ProgressBar->set_text(decode('utf8',gettext("Reinstalling"))." ".@{$db{$soft}}[LOCAL_PKG]);
					$Netpkg->Reinstall( $File);				
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_DOWNGRADED ) {
					$ProgressBar->set_text(decode('utf8',gettext("Downgrading"))." ".@{$db{$soft}}[LOCAL_PKG]);
					$Netpkg->Update( $File);
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_UPDATED ) {
					$ProgressBar->set_text(decode('utf8',gettext("Updating"))." ".@{$db{$soft}}[LOCAL_PKG]);
					$Netpkg->Update( $File);
				}elsif ( @{$db{$soft}}[STATUS] == STATUS_NEW ) {
					$ProgressBar->set_text(decode('utf8',gettext("Installing"))." ".@{$db{$soft}}[LOCAL_PKG]);
					$Netpkg->Install( $File);
				}
				
			$Progress = $Progress + $Range ;
			$ProgressBar->set_fraction($Progress);
			Gtk2->main_iteration while ( Gtk2->events_pending );
			}
		}
	}	
	
	$ProgressBar->set_text(decode('utf8',gettext("Refreshing database")));			
	$ProgressBar->set_fraction(0.95);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	$Netpkg->CheckInstalledPkgs(\%db);
	$Netpkg->ProcessDeps(\%db);
	
	$ProgressBar->set_fraction(1);
	$ProgressBar->set_text(decode('utf8',gettext("Done")));
	Gtk2->main_iteration while ( ! Gtk2->events_pending );	
	$ProgressBar->set_fraction(0);
	$ProgressBar->set_text("");
	
	# Check for any .new files
	if ( $ProcessDotNew ) { &CleanDotNew }
	
	
	# Checking install result !!
	my 	$BadPkgs = 0;
	my 	$GoodPkgs = 0;
	my 	$GoodList;
	my 	$BadList;
		
	for $soft ( keys %db ) {
		if ( defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			next if ( @{$db{$soft}}[BLACKLISTED] );
			
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) { 
				if ( @{$db{$soft}}[LOCAL_PKG] eq @{$db{$soft}}[INSTALLED_PKG] ) {
					$GoodPkgs++;
					if ( $GoodPkgs % 2 == 0 ){
						$GoodList = $GoodList.@{$db{$soft}}[INSTALLED_PKG]."\n";
					}else{
						$GoodList = $GoodList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}
				}else{
					$BadPkgs++;	
					if ( $BadPkgs % 2 == 0 ){
						$BadList = $BadList.@{$db{$soft}}[INSTALLED_PKG]." ";
					}else{
						$BadList = $BadList.@{$db{$soft}}[INSTALLED_PKG]."  ";
					}				
				}	
			}
		}
		
		# Switching off the selection
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
	}
	
	my $GoodListMarkup;
	if ( $GoodPkgs == 0 ) { 
		$GoodListMarkup = "";
	}else{
		$GoodListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Successfully installed:"))." </b></span>\n"."<span size=\"small\"> ".$GoodList."</span>\n" ;
	}
	
	my $BadListMarkup;
	if ( $BadPkgs == 0 ) { 
		$BadListMarkup = "";
	}else{
		$BadListMarkup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Failed to install:"))." </b></span>\n"."<span size=\"small\"> ".$BadList."</span>\n" ;	
	}
	
	
	if ( $GoodPkgs > 0 || $BadPkgs > 0 ) { 
		
		&Dialog(decode('utf8',gettext("Report")), 
			decode('utf8',gettext("Install process result")),
			 $GoodListMarkup.$BadListMarkup, 
			 420, 
			 100,
			 50,
			 NULL,
			 NULL);
	}	
	
	return 0 ;	
}

####################################################################
# Will uninstall selected packages if they are installed ;)
sub UninstallPkgs{
	
	my $ProcessType = shift ;
	
	my $Npkgs = 0 ;
	my 	$PkgList;
	my $soft;
	for $soft ( keys %db ) {
		if ( @{$db{$soft}}[STATUS] != STATUS_NEW && defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) {
				$Npkgs++;	
				if ( $Npkgs % 2 == 0 ){
					$PkgList = $PkgList.@{$db{$soft}}[INSTALLED_PKG]."\n";
				}else{
					$PkgList = $PkgList.@{$db{$soft}}[INSTALLED_PKG]."  ";
				}			
			}
		}
	}
	
	my $Markup;
	if ( $Npkgs == 0 ) { 
		$Markup = "";
	}else{
		$Markup = "<span size=\"medium\" foreground=\"DarkRed\" ><b>".decode('utf8',gettext("Uninstalled packages:"))." </b></span>\n"."<span size=\"small\"> ".$PkgList."</span>\n" ;
	}
	
	if ($Npkgs == 0) { $Npkgs = 1 }
	
	$ProgressBar->set_fraction(0);
	my $Range = 0.85 / $Npkgs  ;
	my $Progress = 0.05;
	$ProgressBar->set_fraction($Progress);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	for $soft ( keys %db ) {
		if ( @{$db{$soft}}[STATUS] != STATUS_NEW && defined @{$db{$soft}}[INSTALLED_PKG] ) { 
			if ( @{$db{$soft}}[SELECTED_AS_PKG] == 1 
			|| ( @{$db{$soft}}[SELECTED_AS_DEP] == 1 && $ProcessType == PROCESS_PKGS_AND_DEPS ) ) {
			
				$ProgressBar->set_text(decode('utf8',gettext("Removing"))." ".@{$db{$soft}}[INSTALLED_PKG]);
				$Netpkg->Remove($soft);
				
				# %db cleanup
				if ( defined @{$db{$soft}}[AVAILABLE_PKG]) {
					@{$db{$soft}}[STATUS] = STATUS_NEW ;
					@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
					@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
					@{$db{$soft}}[INSTALLED_VERSION] = decode('utf8',gettext("not installed")) ;	
					@{$db{$soft}}[INSTALLED_BUILD] = "" ;	
					@{$db{$soft}}[INSTALLED_PKG] = "" ;			
				}else{
					delete($db{$soft});
				}
				
			$Progress = $Progress + $Range ;
			$ProgressBar->set_fraction($Progress);
			Gtk2->main_iteration while ( Gtk2->events_pending );
			}
		}
	}	
	
	$ProgressBar->set_text(decode('utf8',gettext("Refreshing database")));			
	$ProgressBar->set_fraction(0.9);
	Gtk2->main_iteration while ( Gtk2->events_pending );
	
	$Netpkg->CheckInstalledPkgs(\%db);
	$Netpkg->ProcessDeps(\%db);
				
	$ProgressBar->set_fraction(0.95);	
	Gtk2->main_iteration while ( Gtk2->events_pending );
	&FillTree();
	
	$ProgressBar->set_fraction(1);
	$ProgressBar->set_text(decode('utf8',gettext("Done")));
	Gtk2->main_iteration while ( ! Gtk2->events_pending );	
	$ProgressBar->set_fraction(0);
	$ProgressBar->set_text("");
	
	# Uninstall report !!
	&Dialog(decode('utf8',gettext("Report")), 
			decode('utf8',gettext("Uninstall process result")),
			 $Markup, 
			 420, 
			 100,
			 50,
			 NULL, 
			 NULL);
	
	return 0 ;		
}



####################################################################
# called when the corresponding filter is checked 
sub Dialog {
	my ( $title, $subtitle, $Markup, $X, $Y, $chars, $ActionCallback, $ExitCallback ) = @_ ;
	
	my $DialogWindow = Gtk2::Window->new('toplevel') ;

	# Configuring the window
	if ($ExitCallback != NULL) { 
		$DialogWindow->signal_connect(destroy => $ExitCallback);
	}else{
		$DialogWindow->signal_connect(destroy => sub{ return 0; });
	}
	$DialogWindow->set_default_size ($X + 10, $Y + 10 );
	$DialogWindow->set_resizable(TRUE);
	$DialogWindow->set_title("$title");
	$DialogWindow->set_border_width(7);
	$DialogWindow->set_icon_from_file("$WindowIconPath");
	#$DialogWindow->set_position("center");
	
	# info section
	my $InfoLabel = Gtk2::Label->new;
	$InfoLabel->set_width_chars($chars);
	$InfoLabel->set_justify('GTK_JUSTIFY_FILL');
	$InfoLabel->set_line_wrap (TRUE);
	$InfoLabel->set_line_wrap_mode ('word');
	$InfoLabel->set_markup($Markup);
	$InfoLabel->set_selectable(TRUE);
	$InfoLabel->can_focus(0);
	
	my $ScrollLabel = Gtk2::ScrolledWindow->new (undef, undef);
	$ScrollLabel->set_shadow_type ('etched-out');
	$ScrollLabel->set_policy ('automatic', 'automatic');
	$ScrollLabel->set_size_request ($X, $Y);
	$ScrollLabel->add_with_viewport($InfoLabel);
		
	# Actions ##################################"
	my $ExitButton = Gtk2::Button->new_from_stock('gtk-close');
	$ExitButton->signal_connect(clicked => sub{ $DialogWindow->destroy; } ) ;
	
	my $ActionBox = Gtk2::HBox->new;		

	if ( $ActionCallback != NULL ) {
		my $PulseBar = Gtk2::ProgressBar->new;
		$PulseBar->set_fraction(0.0);
		$PulseBar->set_text("");
		$PulseBar->set_pulse_step(0.01);	
		my $OkButton = Gtk2::Button->new_from_stock('gtk-execute');
		$OkButton->signal_connect(clicked => sub{ 
				$semaphorus = 0;
				my $thr = threads->new($ActionCallback);
				while ($semaphorus == 0) {
					$PulseBar->pulse ;
					Gtk2->main_iteration while ( Gtk2->events_pending  );  
				} 
				$PulseBar->set_fraction(0.0);
			} ) ;
		$ActionBox->pack_start($PulseBar, TRUE, TRUE, 5);
		$ActionBox->pack_start($OkButton, TRUE, TRUE, 5);
		
	}	
	
	$ActionBox->pack_end($ExitButton, FALSE, FALSE, 0);		

	my $OuterBox = Gtk2::VBox->new;
	$OuterBox->pack_start($ScrollLabel, TRUE, TRUE, 0);
	$OuterBox->pack_start($ActionBox, FALSE, FALSE, 0);
	$DialogWindow->add($OuterBox);		
	$DialogWindow->show_all;
	return 0 ;	
}

####################################################################
# Will search .new files in /etc and rename them
sub CleanDotNew{
	$semaphorus = 0;
	
	# Check for any .new files
	
	use vars qw/*name *dir *prune/;
	*name   = *File::Find::name;
	*dir    = *File::Find::dir;
	*prune  = *File::Find::prune;
	
	my %DotNews;
	
	File::Find::find({wanted => sub { $DotNews{"$name"} = $dir if (/^.*\.new\z/s)   } }, '/etc');	
	
	my $DotNewFile;
	my $NewFile;
	for $DotNewFile ( keys %DotNews ) { 
		$DotNewFile =~ /^(.*)\.new$/ ;
		$NewFile = $1;
		if ( $Netpkg->{Protected_files} =~ /$NewFile/ ) { next } ;
		# print $DotNewFile." -> ".$NewFile."\n";
		rename ("$DotNewFile", "$NewFile");
	}
	$semaphorus = 1;
	return 0 ;	
}


####################################################################
sub CleanCache {
	my $command = shift;
	$semaphorus = 0;	
	`rm -rf $Netpkg->{Local_repository}`;
    if ($? == -1) {
    	$semaphorus = 1;
		return -1; 
	} else {
		$semaphorus = 1;
		return 0; 
	} 
}
