#!/usr/bin/perl
# -*- coding: utf-8 -*-
#*****************************************************************************
#
#  Copyright (c) 2002 Guillaume Cottenceau
#  Copyright (c) 2002-2008 Thierry Vignaud <tvignaud@mandriva.com>
#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
#  Copyright (c) 2005-2008 Mandriva SA
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License version 2, as
#  published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#*****************************************************************************
#
# $Id: rpmdrake 272803 2011-05-27 05:44:00Z peroyvind $


use MDK::Common::Func 'any';
use lib qw(/usr/lib/libDrakX);
use common;
use utf8;

use Rpmdrake::init;
use standalone;  #- standalone must be loaded very first, for 'explanations', but after rpmdrake::init
use rpmdrake;
use Rpmdrake::open_db;
use Rpmdrake::gui;
use Rpmdrake::rpmnew;
use Rpmdrake::formatting;
use Rpmdrake::pkg;
use urpm::media;

use mygtk2 qw(gtknew);  #- do not import anything else, especially gtkadd() which conflicts with ugtk2 one
use ugtk2 qw(:all);
use Gtk2::Gdk::Keysyms;
use Rpmdrake::widgets;
use feature 'state';

$ugtk2::wm_icon = get_icon('installremoverpm', "title-$MODE");

our $w;
our $statusbar;

my %elems;

sub do_search($$$$$$$) {
    my ($find_entry, $tree, $tree_model, $options, $current_search_type, $urpm, $pkgs) = @_;
    my $entry = $find_entry->get_text or return;
    if (!$use_regexp->[0]) {
        $entry = quotemeta $entry;
        # enable OR search by default:
        $entry =~ s/\\ /|/g if $current_search_type eq 'normal';
    }
    # remove leading/trailing spacing when pasting:
    if ($entry !~ /\S\s\S/) {
        # if spacing in middle, likely a string search in description
        $entry =~ s/^\s*//;
        $entry =~ s/^\s*$//;
    }
    my $entry_rx = eval { qr/$entry/i } or return;
    reset_search();
    $options->{state}{flat} and $options->{delete_all}->();
    $tree->collapse_all;
    my @search_results;
    if ($current_search_type eq 'normal') {
        my $count;
        foreach (@filtered_pkgs) {
            if ($NVR_searches->[0]) {
                next if !/$entry_rx/;
            } else {
                next if first(split_fullname($_)) !~ /$entry_rx/;
            }
            push @search_results, $_;
            # FIXME: should be done for all research types
            last if $count++ > 2000;
        }
    } elsif ($current_search_type eq 'summaries') {
        my $count;
        foreach (@filtered_pkgs) {
            next if get_summary($_) !~ /$entry_rx/;
            push @search_results, $_;
            # FIXME: should be done for all research types
            last if $count++ > 2000;
        }
    } else {
	    my $searchstop;
	    my $searchw = ugtk2->new(N("Software Management"), grab => 1, transient => $w->{real_window});
	    gtkadd(
		$searchw->{window},
		gtkpack__(
		    gtknew('VBox', spacing => 5),
		    gtknew('Label', text => N("Please wait, searching...")),
		    my $searchprogress = gtknew('ProgressBar', width => 300),
		    gtkpack__(
			gtknew('HButtonBox', layout => 'spread'),
			gtksignal_connect(
			    Gtk2::Button->new(but(N("Stop"))),
			    clicked => sub { $searchstop = 1 },
			),
		    ),
		),
	    );
	    $searchw->sync;
            # should probably not account backports packages or find a way to search them:
            my $total_size = keys %$pkgs;
	    my $progresscount;

            my $update_search_pb = sub {
                $progresscount++;
                if (!($progresscount % 100)) {
                    $progresscount <= $total_size and $searchprogress->set_fraction($progresscount/$total_size);
                    $searchw->flush; # refresh and handle clicks
                }
            };
            foreach my $medium (grep { !$_->{ignore} } @{$urpm->{media}}) {
                $searchstop and last;
                my $gurpm; # per medium download progress bar (if needed)
                my $_gurpm_clean_guard = before_leaving { undef $gurpm };
                my $xml_info_file = 
                  urpm::media::any_xml_info($urpm, $medium,
                                            ($current_search_type eq 'files' ? 'files' : 'info'),
                                            undef, 
                                            sub {
                                                $gurpm ||= Rpmdrake::gurpm->new(N("Please wait"),
                                                                                transient => $::main_window);
                                                download_callback($gurpm, @_) or do {
                                                    $searchstop = 1;
                                                };
                                            });
                if (!$xml_info_file) {
                    $urpm->{error}(N("no xml-info available for medium \"%s\"", $medium->{name}));
                    next;
                }
                $searchstop and last;

                require urpm::xml_info;
                require urpm::xml_info_pkg;

                $urpm->{log}("getting information from $xml_info_file");
                if ($current_search_type eq 'files') {
                    # special version for speed (3x faster), hopefully fully compatible
                    my $F = urpm::xml_info::open_lzma($xml_info_file);
                    my $fn;
                    local $_;
                    while (<$F>) {
                        if ($searchstop) {
                            statusbar_msg(N("Search aborted"), 1);
                            goto end_search;
                        }
                        if (m!^<!) { 
                            ($fn) = /fn="(.*)"/;
                            $update_search_pb->();
                        } elsif (/$entry_rx/) {
                            $fn or $urpm->{fatal}("fast algorithm is broken, please report a bug");
                            push @search_results, $fn;
                        }
                    }
                } else {
                    eval {
                        urpm::xml_info::do_something_with_nodes(
                            'info',
                            $xml_info_file,
                            sub {
                                $searchstop and die 'search aborted';
                                my ($node) = @_;
                                $update_search_pb->();
                                push @search_results, $node->{fn} if $node->{description} =~ $entry_rx;
                                #$searchstop and last;
                                return 0 || $searchstop;
                            },
                        );
                    };
                    my $err = $@;
                    if ($err =~ /search aborted/) {
                        statusbar_msg(N("Search aborted"), 1);
                    }
                }
            }

          end_search:
	    @search_results = uniq(@search_results); #- there can be multiple packages with same version/release for different arch's
 	    @search_results = intersection(\@search_results, \@filtered_pkgs);
	    $searchw->destroy;
    }

    my $iter;
    if (@search_results) {
        $elems{$results_ok} = [ map { [ $_, $results_ok ] } sort { uc($a) cmp uc($b) } @search_results ];
        $iter = $options->{add_parent}->($results_ok);
	$options->{add_nodes}->(map { [ $_, $results_ok . ($options->{tree_mode} eq 'by_presence'
								 ? '|' . ($pkgs->{$_}{pkg}->flag_installed ? N("Upgradable") : N("Addable"))
								 : ($options->{tree_mode} eq 'by_selection'
								    ? '|' . ($pkgs->{$_}{selected} ? N("Selected") : N("Not selected"))
								    : ''))
				      ] } sort { uc($a) cmp uc($b) } @search_results);
    } else {
        $iter = $options->{add_parent}->($results_none);
        # clear package list:
        $options->{add_nodes}->();
        my $string = $default_list_mode eq 'all' && $filter->[0] eq 'all' ? N("No search results.") :
          N("No search results. You may want to switch to the '%s' view and to the '%s' filter",
            N("All"), N("All"),);
        statusbar_msg($string , 1);
        gtkset_mousecursor_normal($::w->{rwindow}->window);
    }
    my $tree_selection = $tree->get_selection;
    if (my $path = $tree_model->get_path($iter)) {
        $tree_selection->select_path($path);
        $tree->scroll_to_cell($path, undef, 1, 0.5, 0);
        $tree_selection->signal_emit('changed');
    }
}

sub quit() {
    ($rpmdrake_width->[0], $rpmdrake_height->[0]) = $::w->{real_window}->get_size();
    real_quit();
}

sub run_treeview_dialog {
    my ($callback_action) = @_;

    my ($options, $tree, $tree_model, $detail_list, $detail_list_model);
    (undef, $size_free) = MDK::Common::System::df('/usr');

    $::main_window = $w->{real_window};

    $options = {
	build_tree => sub { build_tree($tree, $tree_model, \%elems, $options, $force_rebuild, @_) },
	partialsel_unsel => sub {
	    my ($unsel, $sel) = @_;
	    @$sel = grep { exists $pkgs->{$_} } @$sel;
	    @$unsel < @$sel;
	},
	get_status => sub {
		N("Selected: %s / Free disk space: %s", formatXiB($size_selected), formatXiB($size_free*1024));
	},
	rebuild_tree => sub {},
    };

    $tree_model = Gtk2::TreeStore->new("Glib::String", "Glib::String", "Gtk2::Gdk::Pixbuf");
    $tree_model->set_sort_column_id($grp_columns{label}, 'ascending');
    $tree = Gtk2::TreeView->new_with_model($tree_model);
    $tree->get_selection->set_mode('browse');

    $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::MDV::CellRendererPixWithLabel->new, 'pixbuf' => $grp_columns{icon}, label => $grp_columns{label}));
    $tree->set_headers_visible(0);

    $detail_list_model = Gtk2::ListStore->new("Glib::String",
                                              "Gtk2::Gdk::Pixbuf",
                                              "Glib::String",
                                              "Glib::Boolean",
                                              "Glib::String",
                                              "Glib::String",
                                              "Glib::String",
                                              "Glib::String", 
                                              "Glib::Boolean");

    $detail_list = Gtk2::TreeView->new_with_model($detail_list_model);
    $detail_list->append_column(
        my $col_sel = Gtk2::TreeViewColumn->new_with_attributes(
            undef,
            Gtk2::CellRendererToggle->new,
            active => $pkg_columns{selected},
            activatable => $pkg_columns{selectable}
        ));
    $col_sel->set_fixed_width(34); # w/o this the toggle cells are not displayed
    $col_sel->set_sizing('fixed');
    $col_sel->set_sort_column_id($pkg_columns{selected});

    my $display_arch_col = to_bool(arch() =~ /64/);
    my @columns = (qw(name version release), if_($display_arch_col, 'arch'));

    my %columns = (
        'name' => {
            title => N("Package"),
            markup => $pkg_columns{short_name},
        },
        'version' => {
            title => N("Version"),
            text => $pkg_columns{version},
        },
        'release' => {
            title => N("Release"),
            text => $pkg_columns{release},
        },
        if_($display_arch_col, 'arch' => {
            title =>
              #-PO: "Architecture" but to be kept *small* !!!
              N("Arch."),
            text => $pkg_columns{arch},
        }),
    );
    foreach my $col (@columns{@columns}) {
        $detail_list->append_column(
            $col->{widget} =
              Gtk2::TreeViewColumn->new_with_attributes(
                  ' ' . $col->{title} . ' ',
                  $col->{renderer} = Gtk2::CellRendererText->new,
                  ($col->{markup} ? (markup => $col->{markup}) : (text => $col->{text})),
              )
            );
        $col->{widget}->set_sort_column_id($col->{markup} || $col->{text});
    }
    $columns{$_}{widget}->set_sizing('autosize') foreach @columns;
    $columns{name}{widget}->set_property('expand', '1');
    $columns{name}{renderer}->set_property('ellipsize', 'end');
    $columns{$_}{renderer}->set_property('xpad', '6') foreach @columns;
    $columns{name}{widget}->set_resizable(1); 
    #$detail_list_model->set_sort_column_id($pkg_columns{text}, 'ascending');
    $detail_list_model->set_sort_func($pkg_columns{version}, \&sort_callback);
    $detail_list->set_rules_hint(1);

    $detail_list->append_column(
        my $pixcolumn =
          Gtk2::TreeViewColumn->new_with_attributes(
              #-PO: "Status" should be kept *small* !!!
              N("Status"),
              my $rdr = Gtk2::CellRendererPixbuf->new,
              'pixbuf' => $pkg_columns{state_icon})
        );
    $rdr->set_fixed_size(34, 24);
    $pixcolumn->set_sort_column_id($pkg_columns{state});

    compute_main_window_size($w);

    my $cursor_to_restore;
    $_->signal_connect(
	expose_event => sub {
	    $cursor_to_restore or return;
	    gtkset_mousecursor_normal($tree->window);
	    undef $cursor_to_restore;
	},
    ) foreach $tree, $detail_list;
    $tree->get_selection->signal_connect(changed => sub {
        my ($model, $iter) = $_[0]->get_selected;
        return if !$iter;
        state $current_group;
        my $new_group = $model->get_path_str($iter);
        return if $current_group eq $new_group && !$force_displaying_group;
        undef $force_displaying_group;
        $current_group = $new_group;
        $model && $iter or return;
        my $group = $model->get($iter, 0);
        my $parent = $iter;
        while ($parent = $model->iter_parent($parent)) {
            $group = join('|', $model->get($parent, 0), $group);
        }
        $detail_list->window->freeze_updates;
        $options->{add_nodes}->(@{$elems{$group}});
        $detail_list->window->thaw_updates if $detail_list->window;
    });

    $options->{state}{splited} = 1;
    $options->{state}{flat} = $tree_flat->[0];

    my $is_backports = get_inactive_backport_media(fast_open_urpmi_db());

    my %filters = (all => N("All"),
                   installed => N("Installed"),
                   non_installed => N("Not installed"),
               );

    my %rfilters = reverse %filters;


    # handle migrating config file from rpmdrake <= 4.9
    if (exists $filters{$default_list_mode}) {
        $filter->[0] = $default_list_mode;
        $default_list_mode = 'all';
    }

    $options->{tree_mode} = $default_list_mode;

    my %modes = (
        flat => N("All packages, alphabetical"),
        by_group => N("All packages, by group"),
        by_leaves => N("Leaves only, sorted by install date"),
        by_presence => N("All packages, by update availability"),
        by_selection => N("All packages, by selection state"),
        by_size => N("All packages, by size"),
        by_source => N("All packages, by medium repository"),
    );


    my %views = (all => N("All"),
                 if_($is_backports, backports =>
                                     #-PO: Backports media are newer but less-tested versions of some packages in main
                                     #-PO: See http://wiki.mandriva.com/en/Policies/SoftwareMedia#.2Fmain.2Fbackports
                                     N("Backports")),
                 meta_pkgs => N("Meta packages"),
                 gui_pkgs => N("Packages with GUI"),
                 all_updates => N("All updates"),
                 security => N("Security updates"),
                 bugfix => N("Bugfixes updates"),
                 normal => N("General updates")
             );
    my %rviews = reverse %views;
    $options->{rviews} = \%rviews;

    my %default_mode = (install => 'all', # we want the new GUI by default instead of "non_installed"
                        remove => 'installed',
                        update => 'security',
                    );
    my %wanted_categories = (
        all_updates => [ qw(security bugfix normal) ],
        security => [ 'security' ],
        bugfix => [ 'bugfix' ],
        normal => [ 'normal' ],
    );
    my $old_value;
    my $view_box = gtknew(
        'ComboBox',
        list => [
            qw(all meta_pkgs gui_pkgs all_updates security bugfix normal),
            if_($is_backports, 'backports')
        ],
        format => sub { $views{$_[0]} }, text => $views{$default_list_mode},
        tip => N("View"),
        changed => sub {
            my $val = $_[0]->get_text;
            return if $val eq $old_value; # workarounding gtk+ sending us sometimes twice events
            $old_value = $val;
            $default_list_mode = $rviews{$val};
            if (my @cat = $wanted_categories{$rviews{$val}} && @{$wanted_categories{$rviews{$val}}}) {
                @$mandrakeupdate_wanted_categories = @cat;
            }

            if ($options->{tree_mode} ne $val) {
                $tree_mode->[0] = $options->{tree_mode} = $rviews{$val};
                $tree_flat->[0] = $options->{state}{flat};
                reset_search();
                switch_pkg_list_mode($rviews{$val});
                $options->{rebuild_tree}->();
            }
        }
    );

    $options->{tree_submode} ||= $default_list_mode;
    $options->{tree_subflat} ||= $options->{state}{flat};


    my $filter_box = gtknew(
        'ComboBox',
        list => [ qw(all installed non_installed) ], text => $filters{$filter->[0]},
        format => sub { $filters{$_[0]} },
        tip => N("Filter"),
        changed => sub {
            state $oldval;
            my $val = $_[0]->get_text;
            return if $val eq $oldval; # workarounding gtk+ sending us sometimes twice events
            $oldval = $val;
            $val = $rfilters{$val};
            if ($filter->[0] ne $val) {
                $filter->[0] = $val;
                reset_search();
                slow_func($::main_window->window, sub { switch_pkg_list_mode($default_list_mode) });
                $options->{rebuild_tree}->();
            }
        }
    );

    my $view_callback = sub {
            my ($val) = @_;
            return if $val eq $old_value; # workarounding gtk+ sending us sometimes twice events
            $old_value = $val;
            return if $mode->[0] eq $val;
            $mode->[0] = $val;
            $tree_flat->[0] = $options->{state}{flat} = member($mode->[0], qw(flat by_leaves by_selection by_size));

            if ($options->{tree_mode} ne $val) {
                reset_search();
                $options->{rebuild_tree}->();
            }
        };


    my @search_types = qw(normal descriptions summaries files);
    my $current_search_type = $search_types[0];
    my $search_menu = Gtk2::Menu->new;
    my $i = 0;
    my $previous;
    foreach (N("in names"), N("in descriptions"), N("in summaries"), N("in file names")) { 
        my ($name, $val) = ($_, $i);
	$search_menu->append(gtksignal_connect(gtkshow(
            $previous = Gtk2::RadioMenuItem->new_with_label($previous, $name)),
                                               activate => sub { $current_search_type = $search_types[$val] }));
        $i++;
    }

    my $info = Gtk2::Mdv::TextView->new;
    $info->set_left_margin(2);
    $info->set_right_margin(15);  #- workaround when right elevator of scrolled window appears

    my $find_callback = sub {
	do_search($find_entry, $tree, $tree_model, $options, $current_search_type, $urpm, $pkgs);
    };

    my $hpaned = gtknew('HPaned', position => $typical_width*0.9,
                        child1 => gtknew('ScrolledWindow', child => $tree),
                        resize1 => 0, shrink1 => 0,
                        resize2 => 1, shrink2 => 0,
                        child2 => gtknew('VPaned',
                                         child1 => gtknew('ScrolledWindow', child => $detail_list), resize1 => 1, shrink1 => 0,
                                         child2 => gtknew('ScrolledWindow', child => $info), resize2 => 1, shrink2 => 0
                                     )
                    );

    my $reload_db_and_clear_all = sub {
        slow_func($w->{real_window}->window, sub {
                      $force_rebuild = 1;
                      pkgs_provider($options->{tree_mode}, skip_updating_mu => 1);
                      reset_search();
                      $size_selected = 0;
                      $options->{rebuild_tree}->();
                      $find_callback->();
                  });
    };

    my $status = gtknew('Label');
    my $checkbox_show_autoselect;
    my %check_boxes;
    my $auto_select_string =
            N("/_Options") . N("/_Select dependencies without asking");
    my $clean_cache_string = 
            N("/_Options") . "/" . 
            N("Clear download cache after successful install");
    my $updates_string = N("/_Options") . N("/_Compute updates on startup");
    my $NVR_string = N("/_Options") . "/" . N("Search in _full package names");
    my $regexp_search_string = N("/_Options") . "/" . N("Use _regular expressions in searches");
    my ($menu, $factory) = create_factory_menu(
	$w->{real_window},
	[ N("/_File"), undef, undef, undef, '<Branch>' ],
	if_(
	    ! $>,
	    [ N("/_File") . N("/_Update media"), undef, sub {
		update_sources_interactive($urpm, transient => $w->{real_window})
		    and $reload_db_and_clear_all->();
	    }, undef, '<Item>' ]
	),
	[ N("/_File") . N("/_Reset the selection"), undef, sub {
	    if ($MODE ne 'remove') {
		$urpm->disable_selected(
		    open_rpm_db(), $urpm->{state},
		    map { if_($pkgs->{$_}{selected}, $pkgs->{$_}{pkg}) } keys %$pkgs,
		);
	    }
	    $pkgs->{$_}{selected} = 0 foreach keys %$pkgs;
	    reset_search();
	    $size_selected = 0;
	    $force_displaying_group = 1;
	    my $tree_selection = $tree->get_selection;
	    $tree_selection->select_path(Gtk2::TreePath->new_from_string('0')) if !$tree_selection->get_selected;
	    $tree_selection->signal_emit('changed');
	}, undef, '<Item>' ],
	[ N("/_File") . N("/Reload the _packages list"), undef, $reload_db_and_clear_all, undef, '<Item>' ],
	[ N("/_File") . N("/_Quit"), N("<control>Q"), \&quit, undef, '<Item>', ],
	#[ N("/_View"), undef, undef, undef, '<Branch>' ],
	if_(!$>,
	    [ N("/_Options"), undef, undef, undef, '<Branch>' ],
	    [ $auto_select_string, undef, 
              sub {
                  my $box = $check_boxes{$auto_select_string};
                  $auto_select->[0] = $box->get_active;
                  $::rpmdrake_options{auto} = $box->get_active;
                  $urpm->{options}{auto} = $box->get_active;
              },
              undef, '<CheckItem>' ],
	    [ $clean_cache_string, undef, 
              sub {
                  $clean_cache->[0] = 
                          $check_boxes{$clean_cache_string}->get_active;
                  $::noclean = !$clean_cache->[0];
              }, 
              undef, '<CheckItem>' ],
	    [ N("/_Options") . N("/_Media Manager"), undef, sub {
               require Rpmdrake::edit_urpm_sources;
               Rpmdrake::edit_urpm_sources::run() && $reload_db_and_clear_all->();
           }, undef, '<Item>' ],
	    [ N("/_Options") . N("/_Show automatically selected packages"), undef, sub {
		$dont_show_selections->[0] = !$checkbox_show_autoselect->get_active;
	    }, undef, '<CheckItem>' ],

	    [ $updates_string, undef, sub {
                $compute_updates->[0] = $check_boxes{$updates_string}->get_active;
	    }, undef, '<CheckItem>' ],
	    [ $NVR_string, undef, sub {
                $NVR_searches->[0] = $check_boxes{$NVR_string}->get_active;
	    }, undef, '<CheckItem>' ],
	    [ $regexp_search_string, undef, sub {
                $use_regexp->[0] = $check_boxes{$regexp_search_string}->get_active;
	    }, undef, '<CheckItem>' ],
	),
	[ N("/_View"), undef, undef, undef, '<Branch>' ],
        (map {
            state ($idx, $previous);
            my $type = $idx ? join('/', N("/_View"), $previous) : '<RadioItem>';
            $type =~ s/_//g; # gtk+ retrieve widgets by their path w/o any shortcut marks
            $previous = $modes{$_};
            $idx++;
            my $val = $_;
            [ N("/_View") . '/' . $modes{$_}, undef, sub { $view_callback->($val) }, 0, $type ];
        } qw(flat by_group by_leaves by_presence by_selection by_size by_source)),
	[ N("/_Help"), undef, undef, undef, '<Branch>' ],
     [ N("/_Help") . N("/_Report Bug"), undef, sub { run_drakbug('rpmdrake') }, undef, '<Item>' ],
	[ N("/_Help") . N("/_Help"), undef, sub { rpmdrake::open_help('') }, undef, '<Item>' ],
     [ N("/_Help") . N("/_About..."), undef, sub {
         my $license = formatAlaTeX(translate($::license));
         $license =~ s/\n/\n\n/sg; # nicer formatting
         my $w = gtknew('AboutDialog', name => N("Rpmdrake"),
                        version => $Rpmdrake::init::version,
                        copyright => N("Copyright (C) %s by Mandriva", '2002-2009'),
                        license => $license, wrap_license => 1,
                        comments => N("Rpmdrake is Mandriva Linux package management tool."),
                        website => 'http://mandrivalinux.com',
                        website_label => N("Mandriva Linux"),
                        authors => 'Thierry Vignaud <vignaud@mandriva.com>',
                        artists => 'Hélène Durosini <ln@mandriva.com>',
                        translator_credits =>
                          #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>")
                          N("_: Translator(s) name(s) & email(s)\n"),
                        transient_for => $::main_window, modal => 1, position_policy => 'center-on-parent',
                    );
         $w->show_all;
         $w->run;
       }, undef, '<Item>'
     ]

    );

    # to retrieve a path, one must prevent "accelerators completion":
    my $get_path = sub { return join('', map { my $i = $_; $i =~ s/_//g; $i } @_) };

    if (my $widget = $factory->get_item('<main>' . $get_path->(N("/_View") . '/' . $modes{$mode->[0]}))) {
        $widget->set_active(1);
    } else {
        warn "Impossible to set $mode->[0] view as default\n";
    }

    %check_boxes = map {
        $_ => $factory->get_widget("<main>" . $get_path->($_));
    } ($auto_select_string, 
       $clean_cache_string, 
       $NVR_string, 
       $updates_string, 
       $regexp_search_string);

    if (!$>) {
        $check_boxes{$regexp_search_string}->set_active($use_regexp->[0]);
        $check_boxes{$NVR_string}->set_active($NVR_searches->[0]);
        $check_boxes{$auto_select_string}->set_active($auto_select->[0]);
        $check_boxes{$updates_string}->set_active($compute_updates->[0]);
        $check_boxes{$clean_cache_string}->set_active($clean_cache->[0]);
    }

    $checkbox_show_autoselect = $factory->get_widget("<main>" . strip_first_underscore(N("/_Options"), N("/_Show automatically selected packages")))
	and $checkbox_show_autoselect->set_active(!$dont_show_selections->[0]);

    my $accel = Gtk2::AccelGroup->new;
    $accel->connect(Gtk2::Gdk->keyval_from_name('F'), ['control-mask'], ['visible'], sub { $find_entry->grab_focus() });
    $w->{real_window}->add_accel_group($accel);

    gtkadd(
	$w->{window},
	gtkpack_(
	    gtknew('VBox', spacing => 3),
	    0, $menu,
	    if_(second(gtkroot()->get_size()) >= 600, 0, getbanner()),
	    1, gtkadd(
		gtknew('Frame', border_width => 3, shadow_type => 'none'),
		gtkpack_(
		    gtknew('VBox', spacing => 3),
		    0, gtkpack_(
			gtknew('HBox', spacing => 10),
			0, $view_box,
			0, $filter_box,
			0, gtknew('Label', text => N("Find:")),
			1, $find_entry = gtknew('Entry', width => 260,
                                             primary_icon => 'gtk-find',
                                             secondary_icon => 'gtk-clear',
                                             tip => N("Please type in the string you want to search then press the <enter> key"),
                                             'icon-release' => $find_callback,
                                             'icon-press' => sub {
                                                 my (undef, $pos, $event) = @_;
                                                 # emulate Sexy::IconEntry's clear_button:
                                                 if ($pos eq 'secondary') {
                                                     $find_entry->set_text('');
                                                     reset_search();
                                                 }
                                                 return if $pos ne 'primary';
                                                 $search_menu->popup(undef, undef, undef, undef, $event->button, $event->time);
                                             },
			    key_press_event => sub {
				member($_[1]->keyval, $Gtk2::Gdk::Keysyms{Return}, $Gtk2::Gdk::Keysyms{KP_Enter})
				    and $find_callback->();
			    },
                                         ),
		    ),
		    1, $hpaned,
		    0, $status,
		    0, gtkpack_(
			gtknew('HBox', spacing => 20),
			0, gtksignal_connect(
			    Gtk2::Button->new(but_(N("Select all"))),
			    clicked => sub {
                       toggle_all($options, 1);
                   },
			),
			1, gtknew('Label'),
			0, my $action_button = gtksignal_connect(
			    Gtk2::Button->new(but_(N("Apply"))),
			    clicked => sub { do_action($options, $callback_action, $info) },
			),
			0, gtksignal_connect(
			    Gtk2::Button->new(but_(N("Quit"))),
			    clicked => \&quit,
			),
		    ),
		),
	    ),
	    0, $statusbar = Gtk2::Statusbar->new,
	),
    );
    $action_button->set_sensitive(0) if $>;
    $find_entry->grab_focus;

    gtktext_insert($info, [ 
        [ $info->render_icon('gtk-dialog-info', 'GTK_ICON_SIZE_DIALOG', undef) ],
        @{ ugtk2::markup_to_TextView_format(
            formatAlaTeX(join("\n\n\n", format_header(N("Quick Introduction")),
                              N("You can browse the packages through the categories tree on the left."),
                              N("You can view information about a package by clicking on it on the right list."),
                              N("To install, update or remove a package, just click on its \"checkbox\"."))))
      }
    ]);

    $w->{rwindow}->set_default_size($typical_width*2.7, 500) if !$::isEmbedded;
    $find_entry->set_text($rpmdrake_options{search}[0]) if $rpmdrake_options{search};

    if ($rpmdrake_width->[0] && $rpmdrake_height->[0]) {
        # so that we can shrink back:
        $w->{real_window}->set_default_size($rpmdrake_width->[0], $rpmdrake_height->[0]);
    }
    $w->{rwindow}->show_all;
    $w->{rwindow}->set_sensitive(0);

    # ensure treeview get realized so that ->get_selection returns something
    $detail_list->realize;
    gtkflush();

    slow_func($::main_window->window, sub { pkgs_provider($default_list_mode) }); # default mode
    if (@initial_selection) {
        $options->{initial_selection} = \@initial_selection;
        $pkgs->{$_}{selected} = 0 foreach @initial_selection;
    }

    $w->{rwindow}->set_sensitive(1);

    $options->{widgets} = {
	w => $w,
	tree => $tree,
	tree_model => $tree_model,
	detail_list_model => $detail_list_model,
	detail_list => $detail_list,
	info => $info,
	status => $status,
    };
    $options->{init_callback} = $find_callback if $rpmdrake_options{search};

    ask_browse_tree_given_widgets_for_rpmdrake($options);
}


# -=-=-=---=-=-=---=-=-=-- main -=-=-=---=-=-=---=-=-=-


if (my $pid = is_running('rpmdrake')) {
    interactive_msg(N("Warning"), N("rpmdrake is already running (pid: %s)", $pid), yesno => [ N("Quit") ]);
    exit(0);
}

$w = ugtk2->new(N("Software Management"));
$w->{rwindow}->show_all if $::isEmbedded;

readconf();

warn_about_user_mode();

do_merge_if_needed();


init();

run_treeview_dialog(\&perform_installation);

writeconf();

myexit(0);
