#!/usr/bin/perl #***************************************************************************** # # Copyright (c) 2002 Guillaume Cottenceau # Copyright (c) 2002-2007 Thierry Vignaud # Copyright (c) 2003, 2004, 2005 MandrakeSoft SA # Copyright (c) 2005-2007 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$ use strict; use MDK::Common::Func 'any'; use lib qw(/usr/lib/libDrakX); use common; use utf8; use standalone; use Rpmdrake::init; use rpmdrake; 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; $ugtk2::wm_icon = "title-$MODE"; our $w; my $treeview_dialog_run = 0; our $statusbar; warn_about_user_mode(); open_db(); sub do_search($$$$$$$) { my ($find_entry, $tree, $tree_model, $options, $current_search_type, $urpm, $pkgs) = @_; my $entry = $find_entry->get_text or return; my $entry_rx = eval { qr/$entry/i } or return; my ($results_ok, $results_none) = (N("Search results"), N("Search results (none)")); $options->{delete_category}->($_) foreach $results_ok, $results_none; $options->{state}{flat} and $options->{delete_all}->(); $tree->collapse_all; my @search_results; if ($current_search_type ne 'normal') { if ($MODE eq 'remove') { if ($current_search_type eq 'descriptions') { @search_results = grep { ($pkgs->{$_}{summary} . $pkgs->{$_}{description}) =~ $entry_rx } keys %$pkgs; } else { slow_func_statusbar( N("Please wait, searching..."), $w->{real_window}, sub { $db->traverse(sub { push @search_results, map { if_($_ =~ $entry_rx, urpm_name($_[0])) } $_[0]->files; }); @search_results = grep { exists $pkgs->{$_} } uniq(@search_results); }, ); } } else { my @hdlists = map { my $h = urpm::media::any_hdlist($_); $h =~ s!^file:/+!!; if_(!$_->{ignore} && ($MODE ne 'update' || $_->{update}) && -r $h, $h); } @{$urpm->{media}}; my $total_size = sum( map { my $pack; eval { require MDV::Packdrakeng; $pack = MDV::Packdrakeng->open(archive => $_, quiet => 1) } ? $pack->{toc_f_count} : 0; } @hdlists ); 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; open my $sf, 'parsehdlist --fileswinfo --description --summary ' . join(' ', map { "'$_'" } @hdlists) . ' |'; my ($pkg, $progresscount); local $_; while (<$sf>) { $searchstop and last; if (/^NAME<([^>]+)> VERSION<([^>]+)> RELEASE<([^>]+)> ARCH<([^>]+)>/) { $pkg = "$1-$2-$3.$4"; $progresscount++; $progresscount <= $total_size and $searchprogress->set_fraction($progresscount/$total_size); $searchw->flush; next; } $pkg or next; my (undef, $key, $value) = split ':', $_; if ($current_search_type eq 'descriptions') { $key =~ /^summary|description$/ or next; } else { $key eq 'files' or next; } if ($value =~ $entry_rx) { exists $pkgs->{$pkg} and push @search_results, $pkg; $pkg = ''; } } close $sf; @search_results = uniq(@search_results); #- there can be multiple packages with same version/release for different arch's $searchw->destroy; } } else { @search_results = grep { $_ =~ $entry_rx } keys %$pkgs; } if (@search_results) { $options->{add_nodes}->(map { [ $_, N("Search results") . ($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); my $last_iter = $tree_model->iter_nth_child(undef, $tree_model->iter_n_children(undef) - 1); my $path = $tree_model->get_path($last_iter); $tree->expand_row($path, 0); $tree->scroll_to_cell($path, undef, 1, 0.5, 0); } else { $options->{add_nodes}->([ '', $results_none, { nochild => 1 } ]); } } package Gtk2::Mdv::TextView; sub new { my ($_class) = @_; my $w = mygtk2::gtknew('TextView'); my $time if 0; require Time::HiRes; $w->signal_connect(size_allocate => sub { my ($w, $requisition) = @_; return if !ref($w->{anchors}); return if Time::HiRes::clock_gettime() - $time < 0.200; $time = Time::HiRes::clock_gettime(); foreach my $anchor (@{$w->{anchors}}) { $_->set_size_request($requisition->width-30, -1) foreach $anchor->get_widgets; } 1; }); $w; } 1; package main; sub run_treeview_dialog { my ($callback_action) = @_; my ($options, $compssUsers, $tree, $tree_model, $detail_list, $detail_list_model, %elems); (undef, $size_free) = MDK::Common::System::df('/usr'); $w = ugtk2->new(N("Software Management")); $::main_window = $w->{real_window}; $options = { build_tree => sub { build_tree($tree, $tree_model, \%elems, $options, $force_rebuild, $compssUsers, @_) }, grep_unselected => sub { grep { exists $pkgs->{$_} && !$pkgs->{$_}{selected} } @_ }, partialsel_unsel => sub { my ($unsel, $sel) = @_; @$sel = grep { exists $pkgs->{$_} } @$sel; @$unsel < @$sel; }, get_status => sub { member($default_list_mode, qw(all non_installed)) ? N("Selected: %s / Free disk space: %s", formatXiB($size_selected), formatXiB($size_free*1024)) : N("Selected size: %d MB", $size_selected/(1024*1024)); }, rebuild_tree => sub {}, }; $tree_model = Gtk2::TreeStore->new("Glib::String", "Glib::String", "Gtk2::Gdk::Pixbuf"); $tree_model->set_sort_column_id(0, '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' => 2, label => 0)); $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 1)); $tree->set_headers_visible(0); $detail_list_model = Gtk2::ListStore->new("Glib::String", "Gtk2::Gdk::Pixbuf", "Glib::String"); $detail_list = Gtk2::TreeView->new_with_model($detail_list_model); $detail_list->append_column(my $pixcolumn = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererPixbuf->new, 'pixbuf' => 1)); $pixcolumn->{is_pix} = 1; $detail_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); $detail_list_model->set_sort_column_id(0, 'ascending'); $detail_list->set_headers_visible(0); $detail_list->set_rules_hint(1); ($typical_width) = string_size($tree, translate("Graphical Environment") . "xmms-more-vis-plugins"); $typical_width > 600 and $typical_width = 600; #- try to not being crazy with a too large value $typical_width < 150 and $typical_width = 150; 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; my $current_group if 0; my $new_group = $model->get($iter, 0); 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); } slow_func($tree->window, sub { $options->{add_nodes}->(@{$elems{$group}}) }); }); $options->{state}{splited} = 1; #$options->{state}{flat} = 1; $compssUsers = parse_compssUsers_flat(); my %modes = (all => N("All"), installed => N("Installed"), non_installed => N("Non installed"), mandrake_choices => $rpmdrake::branded ? N("%s choices", $rpmdrake::distrib{system}) : N("Mandriva Linux choices"), if_(0, # let's keep the translated strings (to be resurected as sorting the treeview): N("All packages, alphabetical"), by_presence => N("All packages, by update availability"), by_size => N("All packages, by size"), by_selection => N("All packages, by selection state"), by_leaves => N("Leaves only, sorted by install date"), by_group => N("All packages, by group"), ), all_updates => N("All updates"), security => N("Security updates"), bugfix => N("Bugfixes updates"), normal => N("Normal updates") ); my %rmodes = reverse %modes; $options->{rmodes} = \%rmodes; 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 $cbox = gtksignal_connect(Gtk2::ComboBox->new_with_strings([ @modes{ 'all', if_($compssUsers, 'mandrake_choices'), qw(installed non_installed all_updates security bugfix normal) } ], $modes{$default_mode{$MODE} || 'all'}), 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 = $rmodes{$val}; if (my @cat = $wanted_categories{$rmodes{$val}} && @{$wanted_categories{$rmodes{$val}}}) { @$mandrakeupdate_wanted_categories = @cat; } if (0) { reset_search(); $options->{rebuild_tree}->(); } $options->{state}{flat} = 0; # was 1 only for the "All packages, alphabetical", "All packages, by size", "All packages, by selection state", and "Leaves only, sorted by install date" if ($options->{tree_mode} ne $val) { ($options->{tree_mode}) = $val =~ /^by/ ? $options->{tree_submode} : $rmodes{$val}; $tree_mode->[0] = $options->{tree_mode}; $tree_flat->[0] = $options->{state}{flat}; reset_search(); slow_func($::main_window->window, sub { switch_pkg_list_mode($rmodes{$val}) }); $options->{rebuild_tree}->(); } } ); my $default_radio = $options->{tree_mode} = $default_list_mode; $cbox->set_text($modes{$default_radio}); $options->{tree_submode} ||= $default_radio; $options->{tree_subflat} ||= $options->{state}{flat}; my @search_types = qw(normal descriptions files); my $current_search_type = $search_types[0]; my $search_types_optionmenu = Gtk2::ComboBox->new; { $search_types_optionmenu->set_model(Gtk2::ListStore->new('Glib::String')); my $search_types_renderer = Gtk2::CellRendererText->new; $search_types_optionmenu->pack_start($search_types_renderer, 0); $search_types_optionmenu->set_attributes($search_types_renderer, text => 0); my $iter = $search_types_optionmenu->get_model->iter_nth_child(undef, 0); $iter = $search_types_optionmenu->get_model->insert(0); $search_types_optionmenu->get_model->set($iter, 0, N("in names")); $iter = $search_types_optionmenu->get_model->insert(1); $search_types_optionmenu->get_model->set($iter, 0, N("in descriptions")); $iter = $search_types_optionmenu->get_model->insert(2); $search_types_optionmenu->get_model->set($iter, 0, N("in file names")); $search_types_optionmenu->set_active(0); $search_types_optionmenu->signal_connect( changed => sub { $current_search_type = $search_types[$search_types_optionmenu->get_active]; }, ); } 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 { $clear_button and $clear_button->set_sensitive(1); do_search($find_entry, $tree, $tree_model, $options, $current_search_type, $urpm, $pkgs); }; my $hpaned = gtknew('HPaned', child1 => gtknew('ScrolledWindow', child => $tree, width => $typical_width*0.9) , 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 $status = gtknew('Label'); my $checkbox_show_autoselect; my ($menu, $factory) = create_factory_menu( $w->{real_window}, [ N("/_File"), undef, undef, undef, '' ], if_( ! $>, [ N("/_File") . N("/_Update media"), undef, sub { update_sources_interactive($urpm, transient => $w->{real_window}) and do { $force_rebuild = 1; pkgs_provider({ skip_updating_mu => 1 }, $options->{tree_mode}); reset_search(); $size_selected = 0; $options->{rebuild_tree}->(); }; }, undef, '' ] ), [ N("/_File") . N("/_Reset the selection"), undef, sub { if ($MODE ne 'remove') { $urpm->disable_selected( $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; $tree->get_selection->signal_emit('changed'); }, undef, '' ], [ N("/_File") . N("/Reload the _packages list"), undef, sub { slow_func($::main_window->window, sub { $force_rebuild = 1; $rmodes{pkgs_provider({ skip_updating_mu => 1 }, $options->{tree_mode})}; }); reset_search(); $size_selected = 0; $options->{rebuild_tree}->(); }, undef, '' ], [ N("/_File") . N("/_Quit"), N("Q"), sub { Gtk2->main_quit }, undef, '', ], #[ N("/_View"), undef, undef, undef, '' ], if_(!$>, [ N("/_Options"), undef, undef, undef, '' ], [ N("/_Options") . N("/_Media Manager"), undef, sub { run_program::raw({ detach => 1 }, 'edit-urpm-sources.pl'); }, undef, '' ], [ N("/_Options") . N("/_Show automatically selected packages"), undef, sub { $dont_show_selections = !$checkbox_show_autoselect->get_active; }, undef, '' ], ), [ N("/_Help"), undef, undef, undef, '' ], [ N("/_Help") . N("/_Report Bug"), undef, sub { run_program::raw({ detach => 1 }, 'drakbug', '--report', 'rpmdrake') }, undef, '' ], [ N("/_Help") . N("/_Help"), undef, sub { rpmdrake::open_help($MODE) }, undef, '' ], [ 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 => '2007', copyright => N("Copyright (C) %s by Mandriva", '2002-2006'), 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 ', artists => 'Hélène Durosini ', translator_credits => #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith ") 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, '' ] ); $checkbox_show_autoselect = $factory->get_widget("
" . strip_first_underscore(N("/_Options"), N("/_Show automatically selected packages"))) and $checkbox_show_autoselect->set_active(!$dont_show_selections); gtkadd( $w->{window}, gtkpack_( gtknew('VBox', spacing => 3), 0, $menu, 0, getbanner(), 1, gtkadd( gtknew('Frame', border_width => 3, shadow_type => 'none'), gtkpack_( gtknew('VBox', spacing => 3), 0, gtkpack__( gtknew('HBox', spacing => 10), $cbox, gtknew('Label', text => N("Find:")), $search_types_optionmenu, gtksignal_connect( $find_entry = gtknew('Entry'), key_press_event => sub { $_[1]->keyval == $Gtk2::Gdk::Keysyms{Return} and $find_callback->(); }, ), gtksignal_connect(Gtk2::Button->new(but(N("Search"))), clicked => $find_callback), gtksignal_connect( $clear_button = Gtk2::Button->new(but(N("Clear"))), clicked => sub { reset_search() }, ), ), 1, $hpaned, 0, $status, 0, gtkpack_( gtknew('HBox', spacing => 20), 0, gtksignal_connect( Gtk2::Button->new(but_(N("Help"))), clicked => sub { rpmdrake::open_help($MODE) }, ), 1, gtknew('Label'), 0, gtksignal_connect( Gtk2::Button->new(but_(N("Select all"))), clicked => sub { return if !ref($options->{toggle_all}); $options->{toggle_all}->(1); }, ), 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 => sub { Gtk2->main_quit }, ), ), ), ), 0, $statusbar = Gtk2::Statusbar->new, ), ); $action_button->set_sensitive(0) if $>; $clear_button->set_sensitive(0); $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($options{search}[0]) if $options{search}; $w->{rwindow}->show_all; $w->{rwindow}->set_sensitive(0); 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 $options{search}; $treeview_dialog_run = 1; ask_browse_tree_given_widgets_for_rpmdrake($options); } # -=-=-=---=-=-=---=-=-=-- main -=-=-=---=-=-=---=-=-=- if ($options{'merge-all-rpmnew'}) { my %pkg2rpmnew; my $wait = wait_msg(N("Please wait, searching...")); print "Searching .rpmnew and .rpmsave files...\n"; $db->traverse(sub { my $n = my_fullname($_[0]); $pkg2rpmnew{$n} = [ grep { m|^/etc| && (-r "$_.rpmnew" || -r "$_.rpmsave") } map { chomp_($_) } $_[0]->files ]; }); print "done.\n"; undef $wait; $typical_width = 330; dialog_rpmnew('', %pkg2rpmnew) and print "Nothing to do.\n"; myexit(0); } readconf(); init(); run_treeview_dialog(\&perform_installation); writeconf(); myexit(0);