#!/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 Gnome2::VFS;

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

setpriority(0, $$, 10);

my $version="4.6.2";

#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 SELECTED => 0b00011111;
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;

use constant WITH_PKG_INFOS => 0;
use constant WITH_SIZES => 1;

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 }

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 $IconRefresh = Gtk2::Image->new_from_icon_name ("reload", 'GTK_ICON_SIZE_MENU');
my $RefreshButton = Gtk2::Button->new();
$RefreshButton->set_image ($IconRefresh);
$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 => \&WriteInfoLabel);
$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" if ( $debug == 1 ) ;
			@{$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 ("gtk-clear", '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 ("gtk-cancel", '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 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 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 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 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");
	}
	&WriteInfoLabel;

	$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");
	}
	&WriteInfoLabel;

	$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);

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

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

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

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

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

# $ActionBox ###########################


my $IconInstall = Gtk2::Image->new_from_icon_name ("filesave", 'GTK_ICON_SIZE_BUTTON');
my $MainInstallButton = Gtk2::Button->new();
$MainInstallButton->set_image ($IconInstall);
# $MainInstallButton->set_label (decode('utf8',gettext("Install")));
$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 ("gtk-clear", '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($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 $ProgressBox = Gtk2::HBox->new(FALSE,5);
$ProgressBox->pack_start($ProgressBar, TRUE, TRUE, 5);
$ProgressBox->pack_end($ExitButton, FALSE, TRUE, 5);

# Menu bar ###########################

my $MainMenuBar = Gtk2::MenuBar->new ();

my $MainMenuItem1 = Gtk2::MenuItem->new(decode('utf8',gettext("Menu")));

$MainMenuBar->append ($MainMenuItem1);
my $Menu1 = Gtk2::Menu->new ();
$MainMenuItem1->set_submenu ($Menu1);

my $MenuItemCleanCache = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Clean cache")));
my $IconClear = Gtk2::Image->new_from_icon_name ("gtk-delete", 'GTK_ICON_SIZE_MENU');
$MenuItemCleanCache->set_image($IconClear);
$tooltips->set_tip ($MenuItemCleanCache, decode('utf8',gettext("Clean the local packages cache !")), FALSE);
$MenuItemCleanCache->signal_connect(activate => \&CleanCacheCallback) ;
$Menu1->append($MenuItemCleanCache);
if ( ! -e $Netpkg->{Local_repository} ) { $MenuItemCleanCache->set_sensitive( FALSE ) };

sub CleanCacheCallback {
	$MenuItemCleanCache->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} ) { $MenuItemCleanCache->set_sensitive( TRUE ) };
	return 0 ;
}

my $MenuItemDotNew = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Activate new config")));
my $IconSearch = Gtk2::Image->new_from_icon_name ("folder-saved-search", 'GTK_ICON_SIZE_MENU');
$MenuItemDotNew->set_image($IconSearch);
$tooltips->set_tip ($MenuItemDotNew, decode('utf8',gettext("Search for .new configuration files to be activated")), FALSE);
$MenuItemDotNew->signal_connect(activate => \&DotNewCallback) ;
$Menu1->append($MenuItemDotNew);
&SearchDotNew();

sub SearchDotNew {
	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 =~ /^ *$/ ) {

		$MenuItemDotNew->set_sensitive( FALSE );
	}else{
		$MenuItemDotNew->set_sensitive( TRUE );
	}
	return 0;

}

sub DotNewCallback {
	$MenuItemDotNew->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,
			\&SearchDotNew,
			);
	return 0;
}

my $MenuItemShowLogs = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Show logs")));
my $IconLogs = Gtk2::Image->new_from_icon_name ("gnome-blog", 'GTK_ICON_SIZE_MENU');
$MenuItemShowLogs->set_image($IconLogs);
$tooltips->set_tip ($MenuItemShowLogs, decode('utf8',gettext("Real time log viewer")), FALSE);
$MenuItemShowLogs->signal_connect(activate => \&ShowLogsCallback) ;
$Menu1->append($MenuItemShowLogs);


sub ShowLogsCallback {
	$Netpkg->AsyncRun("xterm -e \"/usr/bin/tail -n50 -f $Netpkg->{Logfile}\"");
	return 0 ;
}

my $MenuItemRefresh = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Reload mirror")));
my $IconRefresh2 = Gtk2::Image->new_from_icon_name ("reload", 'GTK_ICON_SIZE_MENU');
$MenuItemRefresh->set_image($IconRefresh2);
$tooltips->set_tip ($MenuItemRefresh, decode('utf8',gettext("Load or reload mirror content")), FALSE);
$MenuItemRefresh->signal_connect(activate => \&LoadMirror) ;
$Menu1->append($MenuItemRefresh);

my $MenuItemExit = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Exit")));
my $IconExit2 = Gtk2::Image->new_from_icon_name ("exit", 'GTK_ICON_SIZE_MENU');
$MenuItemExit->set_image($IconExit2);
$MenuItemExit->signal_connect(activate => \&MainExit) ;
$Menu1->append($MenuItemExit);

my $MainMenuItem2 = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Packages")));
$MainMenuBar->append ($MainMenuItem2);
my $Menu2 = Gtk2::Menu->new ();
$MainMenuItem2->set_submenu ($Menu2);

my $MenuItemInstall = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Install")));
my $IconInstall2 = Gtk2::Image->new_from_icon_name ("filesave", 'GTK_ICON_SIZE_MENU');
$MenuItemInstall->set_image($IconInstall2);
$tooltips->set_tip ($MenuItemInstall, decode('utf8',gettext("Compute dependencies and then install/upgrade selected packages")), FALSE);
$MenuItemInstall->signal_connect(activate => \&DialogInstall) ;
$Menu2->append($MenuItemInstall);

my $MenuItemUninstall = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Uninstall")));
my $IconUninstall2 = Gtk2::Image->new_from_icon_name ("gtk-clear", 'GTK_ICON_SIZE_MENU');
$MenuItemUninstall->set_image($IconUninstall2);
$tooltips->set_tip ($MenuItemUninstall, decode('utf8',gettext("Compute dependencies and then uninstall selected packages")), FALSE);
$MenuItemUninstall->signal_connect(activate => \&DialogUninstall) ;
$Menu2->append($MenuItemUninstall);


my $MenuItemSelectUpdates = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Select updates")));
my $IconUnselect = Gtk2::Image->new_from_icon_name ("netpkg-upd", 'GTK_ICON_SIZE_MENU');
$MenuItemSelectUpdates->set_image($IconUnselect);
$tooltips->set_tip ($MenuItemSelectUpdates, decode('utf8',gettext("Select all updated packages")), FALSE);
$MenuItemSelectUpdates->signal_connect(activate => \&SelectUpdatesCallback) ;
$Menu2->append($MenuItemSelectUpdates);

sub SelectUpdatesCallback {
	my $soft;
	for $soft ( keys %db ) {
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
	}
	for $soft ( keys %db ) {
		if ( @{$db{$soft}}[STATUS] == STATUS_UPDATED) {
			@{$db{$soft}}[SELECTED_AS_PKG] = 1 ;
		}
	}
	$ShowModified->set_active(TRUE);
	$ShowOrphans->set_active(FALSE);
	$ShowInstalled->set_active(FALSE);
	$ShowNew->set_active(FALSE);
	$ShowOnly = MODIFIED ;

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


my $MenuItemUnselect = Gtk2::ImageMenuItem->new(decode('utf8',gettext("Unselect all")));
my $IconUnselect = Gtk2::Image->new_from_icon_name ("gtk-cancel", 'GTK_ICON_SIZE_MENU');
$MenuItemUnselect->set_image($IconUnselect);
$tooltips->set_tip ($MenuItemUnselect, decode('utf8',gettext("Unselect all previously selected packages")), FALSE);
$MenuItemUnselect->signal_connect(activate => \&UnselectCallback) ;
$Menu2->append($MenuItemUnselect);

sub UnselectCallback {
	my $soft;
	for $soft ( keys %db ) {
		@{$db{$soft}}[SELECTED_AS_PKG] = 0 ;
		@{$db{$soft}}[SELECTED_AS_DEP] = 0 ;
	}
	&FillTree();
	&WriteInfoLabel;
	return 0 ;
}

# 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($MainMenuBar, FALSE, TRUE, 0);
$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);

	&SetAllInsensitive();

	$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 );

		&SetAllSensitive();
		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 );

	&SetAllSensitive();

	$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;
	$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 ( $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 "" );

}


####################################################################
# called when a row is selected
sub WriteInfoLabel {
	my $ChecklistMode = shift;
	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 ) {

		&WriteInfoLabel(WITH_SIZES);

		&SetAllInsensitive();

		# 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{
														&SetAllSensitive();
														$ActionWindow->destroy;
													} ) ;
		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 ) {

		&WriteInfoLabel(WITH_SIZES);

		&SetAllInsensitive();

		# 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{
														&SetAllSensitive();
														$ActionWindow->destroy;
													} ) ;
		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 ) {

		&WriteInfoLabel;

		&SetAllInsensitive();

		# 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{
													&SetAllSensitive();
													$ActionWindow->destroy;
													} ) ;
		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";
}

sub SetAllSensitive {
	$ActionBox->set_sensitive( TRUE );
	$ScrolledWindow->set_sensitive( TRUE );
	$RefreshButton->set_sensitive( TRUE );
	$FilterBox->set_sensitive( TRUE );
	$PatternBox->set_sensitive( TRUE );
	$MenuItemCleanCache->set_sensitive( TRUE );
	$MenuItemInstall->set_sensitive( TRUE );
	$MenuItemUninstall->set_sensitive( TRUE );
}

sub SetAllInsensitive {
	$ActionBox->set_sensitive( FALSE );
	$ScrolledWindow->set_sensitive( FALSE );
	$RefreshButton->set_sensitive( FALSE );
	$FilterBox->set_sensitive( FALSE );
	$PatternBox->set_sensitive( FALSE );
	$MenuItemCleanCache->set_sensitive( FALSE );
	$MenuItemInstall->set_sensitive( FALSE );
	$MenuItemUninstall->set_sensitive( FALSE );
}

####################################################################
# Called at Action window exit to enable Action button
sub ActionExit {
	&SetAllSensitive();
	&FillTree();
	&WriteInfoLabel;
	&CheckCacheClean;
	return 0 ;
}

####################################################################
# Will download and install packages
sub ActionInstallPkgs{

	my $ProcessType = shift;

	$ActionWindow->destroy;
	$TreeView->collapse_all;
	&SetAllInsensitive();
	$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("");
	&FillTree();
	&WriteInfoLabel;
	&SetAllSensitive();
	&CheckCacheClean();
	&SearchDotNew();
	$MainInfoLabel->set_text("");
	$Netpkg->SaveDB(\%db, "bigdb");
	return 0 ;
}

####################################################################
# Will download and install packages
sub ActionInstallLocalPkgs{

	my $ProcessType = shift;

	$ActionWindow->destroy;
	$TreeView->collapse_all;
	&SetAllInsensitive();
	$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("");
	&FillTree();
	&WriteInfoLabel;
	&SetAllSensitive();
	&CheckCacheClean;
	&SearchDotNew();
	$MainInfoLabel->set_text("");
	$Netpkg->SaveDB(\%db, "bigdb");
	return 0 ;
}

####################################################################
# Will uninstall packages WITHOUT deps
sub ActionUninstallPkgs{

	my $ProcessType = shift;

	$ActionWindow->destroy;
	$TreeView->collapse_all;
	&SetAllInsensitive();
	$MainInfoLabel->set_markup("<span foreground=\"DarkRed\" size=\"medium\"><b>".decode('utf8',gettext("Processing packages ..."))."</b></span>\n");
	&UninstallPkgs ($ProcessType);
	$PatternEntry->set_text("");
	&FillTree();
	&WriteInfoLabel;
	&SetAllSensitive();
	&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;
	}
}
