/[soft]/rpmdrake/trunk/Rpmdrake/gui.pm
ViewVC logotype

Annotation of /rpmdrake/trunk/Rpmdrake/gui.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6532 - (hide annotations) (download)
Thu Nov 29 15:05:20 2012 UTC (11 years, 4 months ago) by tv
File size: 43149 byte(s)
(get_string_from_keywords) rename $medium_name as $medium_path for consistency
1 dmorgan 535 package Rpmdrake::gui;
2     #*****************************************************************************
3     #
4     # Copyright (c) 2002 Guillaume Cottenceau
5     # Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
6     # Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
7     # Copyright (c) 2005-2007 Mandriva SA
8     #
9     # This program is free software; you can redistribute it and/or modify
10     # it under the terms of the GNU General Public License version 2, as
11     # published by the Free Software Foundation.
12     #
13     # This program is distributed in the hope that it will be useful,
14     # but WITHOUT ANY WARRANTY; without even the implied warranty of
15     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     # GNU General Public License for more details.
17     #
18     # You should have received a copy of the GNU General Public License
19     # along with this program; if not, write to the Free Software
20     # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21     #
22     #*****************************************************************************
23     #
24     # $Id$
25    
26     use strict;
27     our @ISA = qw(Exporter);
28     use lib qw(/usr/lib/libDrakX);
29    
30     use common;
31     use mygtk2 qw(gtknew); #- do not import gtkadd which conflicts with ugtk2 version
32    
33     use ugtk2 qw(:helpers :wrappers);
34     use rpmdrake;
35     use Rpmdrake::open_db;
36     use Rpmdrake::formatting;
37     use Rpmdrake::init;
38     use Rpmdrake::icon;
39     use Rpmdrake::pkg;
40     use feature 'state';
41    
42     our @EXPORT = qw(
43     $descriptions
44     $find_entry
45     $force_displaying_group
46     $force_rebuild
47     $pkgs
48     $results_ok
49     $results_none
50     $size_free
51     $size_selected
52     $urpm
53     %grp_columns
54     %pkg_columns
55     @filtered_pkgs
56     @initial_selection
57     ask_browse_tree_given_widgets_for_rpmdrake
58     build_tree
59     callback_choices
60     compute_main_window_size
61     do_action
62     get_info
63     get_summary
64     is_locale_available
65     node_state
66     pkgs_provider
67     real_quit
68     reset_search
69     set_node_state
70     sort_callback
71     switch_pkg_list_mode
72     toggle_all
73     toggle_nodes
74     );
75    
76     our ($descriptions, %filters, @filtered_pkgs, %filter_methods, $force_displaying_group, $force_rebuild, @initial_selection, $pkgs, $size_free, $size_selected, $urpm);
77     our ($results_ok, $results_none) = (N("Search results"), N("Search results (none)"));
78    
79     our %grp_columns = (
80     label => 0,
81     icon => 2,
82     );
83    
84     our %pkg_columns = (
85     text => 0,
86     state_icon => 1,
87     state => 2,
88     selected => 3,
89     short_name => 4,
90     version => 5,
91     release => 6,
92     'arch' => 7,
93     selectable => 8,
94     );
95    
96    
97     sub compute_main_window_size {
98     my ($w) = @_;
99     ($typical_width) = string_size($w->{real_window}, translate("Graphical Environment") . "xmms-more-vis-plugins");
100     $typical_width > 600 and $typical_width = 600; #- try to not being crazy with a too large value
101     $typical_width < 150 and $typical_width = 150;
102     }
103    
104     sub get_summary {
105     my ($key) = @_;
106 tv 6088 my $summary = translate($pkgs->{$key}{pkg}->summary);
107 tv 5516 require utf8;
108 tv 3617 utf8::valid($summary) ? $summary : @{[]};
109 dmorgan 535 }
110    
111     sub build_expander {
112     my ($pkg, $label, $type, $get_data, $o_installed_version) = @_;
113     my $textview;
114     gtkadd(
115     gtkshow(my $exp = gtksignal_connect(
116     Gtk2::Expander->new(format_field($label)),
117     activate => sub {
118     state $first;
119     return if $first;
120     $first = 1;
121     slow_func($::main_window->window, sub {
122     extract_header($pkg, $urpm, $type, $o_installed_version);
123     gtktext_insert($textview, $get_data->() || [ [ N("(Not available)") ] ]);
124     });
125     })),
126     $textview = gtknew('TextView')
127     );
128     $exp->set_use_markup(1);
129     $exp;
130     }
131    
132    
133     sub get_advisory_link {
134     my ($update_descr) = @_;
135     my $link = gtkshow(Gtk2::LinkButton->new($update_descr->{URL}, N("Security advisory")));
136     $link->set_uri_hook(\&run_help_callback);
137     [ $link ];
138     }
139    
140     sub get_description {
141     my ($pkg, $update_descr) = @_;
142     @{ ugtk2::markup_to_TextView_format(join("\n",
143     (eval {
144     escape_text_for_TextView_markup_format(
145     $pkg->{description}
146     || $update_descr->{description});
147     } || '<i>' . N("No description") . '</i>')
148     )) };
149     }
150    
151     sub get_string_from_keywords {
152 tv 3609 my ($medium, $name) = @_;
153     my @media_types;
154     if ($medium->{mediacfg}) {
155 tv 6532 my ($distribconf, $medium_path) = @{$medium->{mediacfg}};
156     @media_types = split(':', $distribconf->getvalue($medium_path, 'media_type')) if $distribconf;
157 tv 3609 }
158 dmorgan 535
159 dmorgan 594 my $unsupported = N("It is <b>not supported</b> by Mageia.");
160 dmorgan 535 my $dangerous = N("It may <b>break</b> your system.");
161     my $s;
162     $s .= N("This package is not free software") . "\n" if member('non-free', @media_types);
163 tv 3611 if ($pkgs->{$name}{is_backport} || member('backport', @media_types)) {
164 dmorgan 535 return join("\n",
165     N("This package contains a new version that was backported."),
166     $unsupported, $dangerous, $s);
167     } elsif (member('testing', @media_types)) {
168     return join("\n",
169     N("This package is a potential candidate for an update."),
170     $unsupported, $dangerous, $s);
171     } elsif (member('updates', @media_types)) {
172     return join("\n",
173     (member('official', @media_types) ?
174 tv 1033 N("This is an official update which is supported by Mageia.")
175     : (N("This is an unofficial update."), $unsupported))
176 dmorgan 535 ,
177     $s);
178     } else {
179 dmorgan 594 $s .= N("This is an official package supported by Mageia") . "\n" if member('official', @media_types);
180 dmorgan 535 return $s;
181     }
182     }
183    
184     sub get_main_text {
185 tv 3611 my ($medium, $fullname, $name, $summary, $is_update, $update_descr) = @_;
186 dmorgan 535
187 tv 3611 my $txt = get_string_from_keywords($medium, $fullname);
188 dmorgan 535
189     ugtk2::markup_to_TextView_format(
190     # force align "name - summary" to the right with RTL languages (#33603):
191     if_(lang::text_direction_rtl(), "\x{200f}") .
192     join("\n",
193     format_header(join(' - ', $name, $summary)) .
194     # workaround gtk+ bug where GtkTextView wronly limit embedded widget size to bigger line's width (#25533):
195     "\x{200b} \x{feff}" . ' ' x 120,
196     if_($txt, format_field(N("Notice: ")) . $txt),
197     if_($is_update, # is it an update?
198     format_field(N("Importance: ")) . format_update_field($update_descr->{importance}),
199     format_field(N("Reason for update: ")) . format_update_field(rpm_description($update_descr->{pre})),
200     ),
201     '' # extra empty line
202     ));
203     }
204    
205     sub get_details {
206 tv 5515 my ($pkg, $upkg, $installed_version, $raw_medium) = @_;
207 dmorgan 535 my $a = ugtk2::markup_to_TextView_format(
208     $spacing . join("\n$spacing",
209 tv 5997 format_field(N("Version: ")) . $upkg->EVR,
210 dmorgan 535 ($upkg->flag_installed ?
211     format_field(N("Currently installed version: ")) . $installed_version : ()
212     ),
213     format_field(N("Group: ")) . translate_group($upkg->group),
214     format_field(N("Architecture: ")) . $upkg->arch,
215     format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)),
216     eval { format_field(N("Medium: ")) . $raw_medium->{name} },
217     ),
218     );
219     my @link = get_url_link($upkg, $pkg);
220     push @$a, @link if @link;
221     $a;
222     }
223    
224     sub get_new_deps {
225     my ($urpm, $upkg) = @_;
226     my $deps_textview;
227     my @a = [ gtkadd(
228     gtksignal_connect(
229     gtkshow(my $dependencies = Gtk2::Expander->new(format_field(N("New dependencies:")))),
230     activate => sub {
231     slow_func($::main_window->window, sub {
232     my $state = {};
233 tv 5743 my $db = open_rpm_db();
234 tv 5921 my @requested = $urpm->resolve_requested__no_suggests_(
235 tv 5743 $db, $state,
236 dmorgan 535 { $upkg->id => 1 },
237     );
238 tv 5943 @requested = $urpm->resolve_requested_suggests($db, $state, \@requested);
239 tv 5743 undef $db;
240 dmorgan 535 my @nodes_with_deps = map { urpm_name($_) } @requested;
241     my @deps = sort { $a cmp $b } difference2(\@nodes_with_deps, [ urpm_name($upkg) ]);
242 tv 5953 @deps = N("All dependencies installed.") if !@deps;
243 dmorgan 535 gtktext_insert($deps_textview, join("\n", @deps));
244     });
245     }
246     ),
247     $deps_textview = gtknew('TextView')
248     ) ];
249     $dependencies->set_use_markup(1);
250     @a;
251     }
252    
253     sub get_url_link {
254     my ($upkg, $pkg) = @_;
255    
256     my $url = $upkg->url || $pkg->{url};
257    
258     if (!$url) {
259 tv 5389 open_rpm_db()->traverse_tag_find('name', $upkg->name, sub { $url = $_[0]->url });
260 dmorgan 535 }
261    
262     return if !$url;
263    
264     my @a =
265     (@{ ugtk2::markup_to_TextView_format(format_field("\n$spacing" . N("URL: "))) },
266     [ my $link = gtkshow(Gtk2::LinkButton->new($url, $url)) ]);
267     $link->set_uri_hook(\&run_help_callback);
268     @a;
269     }
270    
271     sub files_format {
272     my ($files) = @_;
273     ugtk2::markup_to_TextView_format(
274     '<tt>' . $spacing #- to highlight information
275     . join("\n$spacing", map { "\x{200e}$_" } @$files)
276     . '</tt>');
277     }
278    
279     sub format_pkg_simplifiedinfo {
280     my ($pkgs, $key, $urpm, $descriptions) = @_;
281     my ($name) = split_fullname($key);
282     my $pkg = $pkgs->{$key};
283     my $upkg = $pkg->{pkg};
284     return if !$upkg;
285     my $raw_medium = pkg2medium($upkg, $urpm);
286     my $medium = !$raw_medium->{fake} ? $raw_medium->{name} : undef;
287     my $update_descr = $descriptions->{$medium}{$name};
288     # discard update fields if not matching:
289     my $is_update = ($upkg->flag_upgrade && $update_descr && $update_descr->{pre});
290     my $summary = get_summary($key);
291 tv 3611 my $s = get_main_text($raw_medium, $key, $name, $summary, $is_update, $update_descr);
292 dmorgan 535 push @$s, get_advisory_link($update_descr) if $is_update;
293    
294     push @$s, get_description($pkg, $update_descr);
295     push @$s, [ "\n" ];
296     my $installed_version = eval { find_installed_version($upkg) };
297    
298     push @$s, [ gtkadd(gtkshow(my $details_exp = Gtk2::Expander->new(format_field(N("Details:")))),
299 tv 5515 gtknew('TextView', text => get_details($pkg, $upkg, $installed_version, $raw_medium))) ];
300 dmorgan 535 $details_exp->set_use_markup(1);
301     push @$s, [ "\n\n" ];
302     push @$s, [ build_expander($pkg, N("Files:"), 'files', sub { files_format($pkg->{files}) }) ];
303     push @$s, [ "\n\n" ];
304     push @$s, [ build_expander($pkg, N("Changelog:"), 'changelog', sub { $pkg->{changelog} }, $installed_version) ];
305    
306     push @$s, [ "\n\n" ];
307     if ($upkg->id) { # If not installed
308     push @$s, get_new_deps($urpm, $upkg);
309     }
310     $s;
311     }
312    
313     sub format_pkg_info {
314     my ($pkgs, $key, $urpm, $descriptions) = @_;
315     my $pkg = $pkgs->{$key};
316     my $upkg = $pkg->{pkg};
317     my ($name, $version) = split_fullname($key);
318     my @files = (
319     format_field(N("Files:\n")),
320     exists $pkg->{files}
321     ? '<tt>' . join("\n", map { "\x{200e}$_" } @{$pkg->{files}}) . '</tt>' #- to highlight information
322     : N("(Not available)"),
323     );
324     my @chglo = (format_field(N("Changelog:\n")), ($pkg->{changelog} ? @{$pkg->{changelog}} : N("(Not available)")));
325     my @source_info = (
326     $MODE eq 'remove' || !@$max_info_in_descr
327     ? ()
328     : (
329     format_field(N("Medium: ")) . pkg2medium($upkg, $urpm)->{name},
330     format_field(N("Currently installed version: ")) . find_installed_version($upkg),
331     )
332     );
333     my @max_info = @$max_info_in_descr && $changelog_first ? (@chglo, @files) : (@files, '', @chglo);
334     ugtk2::markup_to_TextView_format(join("\n", format_field(N("Name: ")) . $name,
335     format_field(N("Version: ")) . $version,
336     format_field(N("Architecture: ")) . $upkg->arch,
337     format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)),
338     if_(
339     $MODE eq 'update',
340     format_field(N("Importance: ")) . $descriptions->{$name}{importance}
341     ),
342     @source_info,
343     '', # extra empty line
344 tv 6088 format_field(N("Summary: ")) . $upkg->summary,
345 dmorgan 535 '', # extra empty line
346     if_(
347     $MODE eq 'update',
348     format_field(N("Reason for update: ")) . rpm_description($descriptions->{$name}{pre}),
349     ),
350     format_field(N("Description: ")), ($pkg->{description} || $descriptions->{$name}{description} || N("No description")),
351     @max_info,
352     ));
353     }
354    
355 tv 3603 sub warn_if_no_pkg {
356 dmorgan 535 my ($name) = @_;
357 tv 3603 my ($short_name) = split_fullname($name);
358     state $warned;
359     if (!$warned) {
360     $warned = 1;
361     interactive_msg(N("Warning"),
362 dmorgan 535 join("\n",
363     N("The package \"%s\" was found.", $name),
364     N("However this package is not in the package list."),
365     N("You may want to update your urpmi database."),
366     '',
367     N("Matching packages:"),
368     '',
369     join("\n", sort map {
370     #-PO: this is list fomatting: "- <package_name> (medium: <medium_name>)"
371     #-PO: eg: "- rpmdrake (medium: "Main Release"
372     N("- %s (medium: %s)", $_, pkg2medium($pkgs->{$_}{pkg}, $urpm)->{name});
373 tv 3603 } grep { /^$short_name/ } keys %$pkgs),
374 dmorgan 535 ),
375     scroll => 1,
376     );
377     }
378 tv 3603 return 'XXX';
379     }
380    
381     sub node_state {
382     my ($name) = @_;
383 tv 3606 #- checks $_[0] -> hack for partial tree displaying
384     return 'XXX' if !$name;
385 tv 3603 my $pkg = $pkgs->{$name};
386     my $urpm_obj = $pkg->{pkg};
387     return warn_if_no_pkg($name) if !$urpm_obj;
388 dmorgan 535 $pkg->{selected} ?
389     ($urpm_obj->flag_installed ?
390     ($urpm_obj->flag_upgrade ? 'to_install' : 'to_remove')
391     : 'to_install')
392     : ($urpm_obj->flag_installed ?
393 tv 3846 ($pkgs->{$name}{is_backport} ? 'backport' :
394 dmorgan 535 ($urpm_obj->flag_upgrade ? 'to_update'
395 tv 3846 : ($urpm_obj->flag_base ? 'base' : 'installed')))
396 dmorgan 535 : 'uninstalled');
397     }
398    
399     my ($common, $w, %wtree, %ptree, %pix);
400    
401     sub set_node_state {
402     my ($iter, $state, $model) = @_;
403 tv 3604 return if $state eq 'XXX' || !$state;
404 dmorgan 535 $pix{$state} ||= gtkcreate_pixbuf('state_' . $state);
405     $model->set($iter, $pkg_columns{state_icon} => $pix{$state});
406     $model->set($iter, $pkg_columns{state} => $state);
407     $model->set($iter, $pkg_columns{selected} => to_bool(member($state, qw(base installed to_install)))); #$pkg->{selected}));
408     $model->set($iter, $pkg_columns{selectable} => to_bool($state ne 'base'));
409     }
410    
411     sub set_leaf_state {
412     my ($leaf, $state, $model) = @_;
413     set_node_state($_, $state, $model) foreach @{$ptree{$leaf}};
414     }
415    
416     sub grep_unselected { grep { exists $pkgs->{$_} && !$pkgs->{$_}{selected} } @_ }
417    
418     sub add_parent {
419     my ($root, $state) = @_;
420     $root or return undef;
421     if (my $w = $wtree{$root}) { return $w }
422     my $s; foreach (split '\|', $root) {
423     my $s2 = $s ? "$s|$_" : $_;
424     $wtree{$s2} ||= do {
425     my $pixbuf = get_icon($s2, $s);
426 tv 3871 my $iter = $w->{tree_model}->append_set($s ? add_parent($s, $state) : undef,
427 dmorgan 535 [ $grp_columns{label} => $_, if_($pixbuf, $grp_columns{icon} => $pixbuf) ]);
428     $iter;
429     };
430     $s = $s2;
431     }
432     set_node_state($wtree{$s}, $state, $w->{tree_model}); #- use this state by default as tree is building. #
433     $wtree{$s};
434     }
435    
436     sub add_node {
437 tv 3872 my ($leaf, $root, $o_options) = @_;
438 dmorgan 535 my $state = node_state($leaf) or return;
439     if ($leaf) {
440     my $iter;
441     if (is_a_package($leaf)) {
442     my ($name, $version, $release, $arch) = split_fullname($leaf);
443     $iter = $w->{detail_list_model}->append_set([ $pkg_columns{text} => $leaf,
444     $pkg_columns{short_name} => format_name_n_summary($name, get_summary($leaf)),
445     $pkg_columns{version} => $version,
446     $pkg_columns{release} => $release,
447     $pkg_columns{arch} => $arch,
448     ]);
449     set_node_state($iter, $state, $w->{detail_list_model});
450     $ptree{$leaf} = [ $iter ];
451     } else {
452     $iter = $w->{tree_model}->append_set(add_parent($root, $state), [ $grp_columns{label} => $leaf ]);
453     push @{$wtree{$leaf}}, $iter;
454     }
455     } else {
456     my $parent = add_parent($root, $state);
457     #- hackery for partial displaying of trees, used in rpmdrake:
458     #- if leaf is void, we may create the parent and one child (to have the [+] in front of the parent in the ctree)
459     #- though we use '' as the label of the child; then rpmdrake will connect on tree_expand, and whenever
460     #- the first child has '' as the label, it will remove the child and add all the "right" children
461 tv 3872 $o_options->{nochild} or $w->{tree_model}->append_set($parent, [ $grp_columns{label} => '' ]); # test $leaf?
462 dmorgan 535 }
463     }
464    
465     my ($prev_label);
466     sub update_size {
467     my ($common) = shift @_;
468     if ($w->{status}) {
469     my $new_label = $common->{get_status}();
470     $prev_label ne $new_label and $w->{status}->set($prev_label = $new_label);
471     }
472     }
473    
474     sub children {
475     my ($w) = @_;
476     map { $w->{detail_list_model}->get($_, $pkg_columns{text}) } gtktreeview_children($w->{detail_list_model});
477     }
478    
479     sub toggle_all {
480     my ($common, $_val) = @_;
481     my $w = $common->{widgets};
482     my @l = children($w) or return;
483    
484     my @unsel = grep_unselected(@l);
485     my @p = @unsel ?
486     #- not all is selected, select all if no option to potentially override
487     (exists $common->{partialsel_unsel} && $common->{partialsel_unsel}->(\@unsel, \@l) ? difference2(\@l, \@unsel) : @unsel)
488     : @l;
489     toggle_nodes($w->{detail_list}->window, $w->{detail_list_model}, \&set_leaf_state, node_state($p[0]), @p);
490     update_size($common);
491     }
492    
493     # ask_browse_tree_given_widgets_for_rpmdrake will run gtk+ loop. its main parameter "common" is a hash containing:
494     # - a "widgets" subhash which holds:
495     # o a "w" reference on a ugtk2 object
496     # o "tree" & "info" references a TreeView
497     # o "info" is a TextView
498     # o "tree_model" is the associated model of "tree"
499     # o "status" references a Label
500     # - some methods: get_info, node_state, build_tree, partialsel_unsel, grep_unselected, rebuild_tree, toggle_nodes, get_status
501     # - "tree_submode": the default mode (by group, ...), ...
502     # - "state": a hash of misc flags: => { flat => '0' },
503     # o "flat": is the tree flat or not
504     # - "tree_mode": mode of the tree ("gui_pkgs", "by_group", ...) (mainly used by rpmdrake)
505    
506     sub ask_browse_tree_given_widgets_for_rpmdrake {
507     ($common) = @_;
508     $w = $common->{widgets};
509    
510     $w->{detail_list} ||= $w->{tree};
511     $w->{detail_list_model} ||= $w->{tree_model};
512    
513     $common->{add_parent} = \&add_parent;
514     my $clear_all_caches = sub {
515     %ptree = %wtree = ();
516     };
517     $common->{clear_all_caches} = $clear_all_caches;
518     $common->{delete_all} = sub {
519     $clear_all_caches->();
520     $w->{detail_list_model}->clear;
521     $w->{tree_model}->clear;
522     };
523     $common->{rebuild_tree} = sub {
524     $common->{delete_all}->();
525     $common->{build_tree}($common->{state}{flat}, $common->{tree_mode});
526     update_size($common);
527     };
528     $common->{delete_category} = sub {
529     my ($cat) = @_;
530     exists $wtree{$cat} or return;
531     %ptree = ();
532    
533     if (exists $wtree{$cat}) {
534     my $_iter_str = $w->{tree_model}->get_path_str($wtree{$cat});
535     $w->{tree_model}->remove($wtree{$cat});
536     delete $wtree{$cat};
537     }
538     update_size($common);
539     };
540     $common->{add_nodes} = sub {
541     my (@nodes) = @_;
542     $w->{detail_list_model}->clear;
543     $w->{detail_list}->scroll_to_point(0, 0);
544     add_node($_->[0], $_->[1], $_->[2]) foreach @nodes;
545     update_size($common);
546     };
547    
548     $common->{display_info} = sub {
549     gtktext_insert($w->{info}, get_info($_[0], $w->{tree}->window));
550     $w->{info}->scroll_to_iter($w->{info}->get_buffer->get_start_iter, 0, 0, 0, 0);
551     0;
552     };
553    
554     my $fast_toggle = sub {
555     my ($iter) = @_;
556     gtkset_mousecursor_wait($w->{w}{rwindow}->window);
557     my $_cleaner = before_leaving { gtkset_mousecursor_normal($w->{w}{rwindow}->window) };
558     my $name = $w->{detail_list_model}->get($iter, $pkg_columns{text});
559     my $urpm_obj = $pkgs->{$name}{pkg};
560    
561     if ($urpm_obj->flag_base) {
562     interactive_msg(N("Warning"),
563     N("Removing package %s would break your system", $name));
564     return '';
565     }
566    
567     if ($urpm_obj->flag_skip) {
568     interactive_msg(N("Warning"), N("The \"%s\" package is in urpmi skip list.\nDo you want to select it anyway?", $name), yesno => 1) or return '';
569     $urpm_obj->set_flag_skip(0);
570     }
571    
572     if ($Rpmdrake::pkg::need_restart && !$priority_up_alread_warned) {
573     $priority_up_alread_warned = 1;
574     interactive_msg(N("Warning"), '<b>' . N("Rpmdrake or one of its priority dependencies needs to be updated first. Rpmdrake will then restart.") . '</b>' . "\n\n");
575     }
576    
577     toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $w->{detail_list_model}->get($iter, $pkg_columns{state}),
578     $w->{detail_list_model}->get($iter, $pkg_columns{text}));
579     update_size($common);
580     };
581     $w->{detail_list}->get_selection->signal_connect(changed => sub {
582     my ($model, $iter) = $_[0]->get_selected;
583     $model && $iter or return;
584     $common->{display_info}($model->get($iter, $pkg_columns{text}));
585     });
586     ($w->{detail_list}->get_column(0)->get_cell_renderers)[0]->signal_connect(toggled => sub {
587     my ($_cell, $path) = @_; #text_
588     my $iter = $w->{detail_list_model}->get_iter_from_string($path);
589     $fast_toggle->($iter) if $iter;
590     1;
591     });
592     $common->{rebuild_tree}->();
593     update_size($common);
594     $common->{initial_selection} and toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, undef, @{$common->{initial_selection}});
595     #my $_b = before_leaving { $clear_all_caches->() };
596     $common->{init_callback}->() if $common->{init_callback};
597     $w->{w}->main;
598     }
599    
600     our $find_entry;
601    
602     sub reset_search() {
603     return if !$common;
604     $common->{delete_category}->($_) foreach $results_ok, $results_none;
605     # clear package list:
606     $common->{add_nodes}->();
607     }
608    
609     sub is_a_package {
610     my ($pkg) = @_;
611     return exists $pkgs->{$pkg};
612     }
613    
614     sub switch_pkg_list_mode {
615     my ($mode) = @_;
616     return if !$mode;
617     return if !$filter_methods{$mode};
618     $force_displaying_group = 1;
619     $filter_methods{$mode}->();
620     }
621    
622     sub is_updatable {
623     my $p = $pkgs->{$_[0]};
624     $p->{pkg} && !$p->{selected} && $p->{pkg}->flag_installed && $p->{pkg}->flag_upgrade;
625     }
626    
627     sub pkgs_provider {
628 tv 3837 my ($mode, %options) = @_;
629 dmorgan 535 return if !$mode;
630 tv 3839 my $h = &get_pkgs(%options);
631 dmorgan 535 ($urpm, $descriptions) = @$h{qw(urpm update_descr)};
632     $pkgs = $h->{all_pkgs};
633     %filters = (
634     non_installed => $h->{installable},
635     installed => $h->{installed},
636     all => [ keys %$pkgs ],
637     );
638     my %tmp_filter_methods = (
639     all => sub {
640 tv 3604 [ difference2([ keys %$pkgs ], $h->{inactive_backports}) ];
641 dmorgan 535 },
642     all_updates => sub {
643     # potential "updates" from media not tagged as updates:
644     if (!$options{pure_updates} && !$Rpmdrake::pkg::need_restart) {
645     [ @{$h->{updates}},
646     difference2([ grep { is_updatable($_) } @{$h->{installable}} ], $h->{backports}) ];
647     } else {
648     [ difference2($h->{updates}, $h->{inactive_backports}) ];
649     }
650     },
651     backports => sub { $h->{backports} },
652     meta_pkgs => sub {
653 tv 3604 [ difference2($h->{meta_pkgs}, $h->{inactive_backports}) ];
654 dmorgan 535 },
655     gui_pkgs => sub {
656 tv 3604 [ difference2($h->{gui_pkgs}, $h->{inactive_backports}) ];
657 dmorgan 535 },
658     );
659     foreach my $importance (qw(bugfix security normal)) {
660     $tmp_filter_methods{$importance} = sub {
661     my @media = keys %$descriptions;
662     [ grep {
663     my ($name) = split_fullname($_);
664     my $medium = find { $descriptions->{$_}{$name} } @media;
665     $medium && $descriptions->{$medium}{$name}{importance} eq $importance } @{$h->{updates}} ];
666     };
667     }
668    
669     undef %filter_methods;
670     foreach my $type (keys %tmp_filter_methods) {
671     $filter_methods{$type} = sub {
672     $force_rebuild = 1; # force rebuilding tree since we changed filter (FIXME: switch to SortModel)
673     @filtered_pkgs = intersection($filters{$filter->[0]}, $tmp_filter_methods{$type}->());
674     };
675     }
676    
677     switch_pkg_list_mode($mode);
678     }
679    
680     sub closure_removal {
681     local $urpm->{state} = {};
682     urpm::select::find_packages_to_remove($urpm, $urpm->{state}, \@_);
683     }
684    
685     sub is_locale_available {
686 tv 3613 my ($name) = @_;
687     any { $urpm->{depslist}[$_]->flag_selected } keys %{$urpm->{provides}{$name} || {}} and return 1;
688 dmorgan 535 my $found;
689 tv 3615 open_rpm_db()->traverse_tag_find('name', $name, sub { $found = 1 });
690 dmorgan 535 return $found;
691     }
692    
693     sub callback_choices {
694     my (undef, undef, undef, $choices) = @_;
695     return $choices->[0] if $::rpmdrake_options{auto};
696     foreach my $pkg (@$choices) {
697     foreach ($pkg->requires_nosense) {
698     /locales-/ or next;
699     is_locale_available($_) and return $pkg;
700     }
701     }
702     my $callback = sub { interactive_msg(N("More information on package..."), get_info($_[0]), scroll => 1) };
703     $choices = [ sort { $a->name cmp $b->name } @$choices ];
704     my @choices = interactive_list_(N("Please choose"), (scalar(@$choices) == 1 ?
705     N("The following package is needed:") : N("One of the following packages is needed:")),
706     [ map { urpm_name($_) } @$choices ], $callback, nocancel => 1);
707     defined $choices[0] ? $choices->[$choices[0]] : undef;
708     }
709    
710     sub deps_msg {
711     return 1 if $dont_show_selections->[0];
712     my ($title, $msg, $nodes, $nodes_with_deps) = @_;
713     my @deps = sort { $a cmp $b } difference2($nodes_with_deps, $nodes);
714     @deps > 0 or return 1;
715     deps_msg_again:
716     my $results = interactive_msg(
717     $title, $msg .
718     format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @deps)
719     . "\n\n" . format_size($urpm->selected_size($urpm->{state})),
720     yesno => [ N("Cancel"), N("More info"), N("Ok") ],
721     scroll => 1,
722     );
723     if ($results eq
724     #-PO: Keep it short, this is gonna be on a button
725     N("More info")) {
726     interactive_packtable(
727     N("Information on packages"),
728     $::main_window,
729     undef,
730     [ map { my $pkg = $_;
731     [ gtknew('HBox', children_tight => [ gtkset_selectable(gtknew('Label', text => $pkg), 1) ]),
732     gtknew('Button', text => N("More information on package..."),
733     clicked => sub {
734     interactive_msg(N("More information on package..."), get_info($pkg), scroll => 1);
735     }) ] } @deps ],
736     [ gtknew('Button', text => N("Ok"),
737     clicked => sub { Gtk2->main_quit }) ]
738     );
739     goto deps_msg_again;
740     } else {
741     return $results eq N("Ok");
742     }
743     }
744    
745     sub toggle_nodes {
746     my ($widget, $model, $set_state, $old_state, @nodes) = @_;
747     @nodes = grep { exists $pkgs->{$_} } @nodes
748     or return;
749     #- avoid selecting too many packages at once
750     return if !$dont_show_selections->[0] && @nodes > 2000;
751     my $new_state = !$pkgs->{$nodes[0]}{selected};
752    
753     my @nodes_with_deps;
754    
755     my $bar_id = statusbar_msg(N("Checking dependencies of package..."), 0);
756    
757     my $warn_about_additional_packages_to_remove = sub {
758     my ($msg) = @_;
759     statusbar_msg_remove($bar_id);
760     deps_msg(N("Some additional packages need to be removed"),
761     formatAlaTeX($msg) . "\n\n",
762     \@nodes, \@nodes_with_deps) or @nodes_with_deps = ();
763     };
764    
765     if (member($old_state, qw(to_remove installed))) { # remove pacckages
766     if ($new_state) {
767     my @remove;
768     slow_func($widget, sub { @remove = closure_removal(@nodes) });
769     @nodes_with_deps = grep { !$pkgs->{$_}{selected} && !/^basesystem/ } @remove;
770     $warn_about_additional_packages_to_remove->(
771     N("Because of their dependencies, the following package(s) also need to be removed:"));
772     my @impossible_to_remove;
773     foreach (grep { exists $pkgs->{$_}{base} } @remove) {
774     ${$pkgs->{$_}{base}} == 1 ? push @impossible_to_remove, $_ : ${$pkgs->{$_}{base}}--;
775     }
776 tv 2028 @impossible_to_remove and interactive_msg(N("Some packages cannot be removed"),
777 dmorgan 535 N("Removing these packages would break your system, sorry:\n\n") .
778     format_list(@impossible_to_remove));
779     @nodes_with_deps = difference2(\@nodes_with_deps, \@impossible_to_remove);
780     } else {
781     slow_func($widget,
782     sub { @nodes_with_deps = grep { intersection(\@nodes, [ closure_removal($_) ]) }
783     grep { $pkgs->{$_}{selected} && !member($_, @nodes) } keys %$pkgs });
784     push @nodes_with_deps, @nodes;
785     $warn_about_additional_packages_to_remove->(
786     N("Because of their dependencies, the following package(s) must be unselected now:\n\n"));
787     $pkgs->{$_}{base} && ${$pkgs->{$_}{base}}++ foreach @nodes_with_deps;
788     }
789     } else {
790     if ($new_state) {
791     if (@nodes > 1) {
792     #- unselect i18n packages of which locales is not already present (happens when user clicks on KDE group)
793     my @bad_i18n_pkgs;
794     foreach my $sel (@nodes) {
795     foreach ($pkgs->{$sel}{pkg}->requires_nosense) {
796     /locales-([^-]+)/ or next;
797     $sel =~ /-$1[-_]/ && !is_locale_available($_) and push @bad_i18n_pkgs, $sel;
798     }
799     }
800     @nodes = difference2(\@nodes, \@bad_i18n_pkgs);
801     }
802     my @requested;
803     slow_func(
804     $widget,
805     sub {
806     @requested = $urpm->resolve_requested(
807     open_rpm_db(), $urpm->{state},
808     { map { $pkgs->{$_}{pkg}->id => 1 } @nodes },
809     callback_choices => \&callback_choices,
810     );
811     },
812     );
813     @nodes_with_deps = map { urpm_name($_) } @requested;
814     statusbar_msg_remove($bar_id);
815     if (!deps_msg(N("Additional packages needed"),
816     formatAlaTeX(N("To satisfy dependencies, the following package(s) also need to be installed:\n\n")) . "\n\n",
817     \@nodes, \@nodes_with_deps)) {
818     @nodes_with_deps = ();
819     $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested);
820     goto packages_selection_ok;
821     }
822    
823     if (my $conflicting_msg = urpm::select::conflicting_packages_msg($urpm, $urpm->{state})) {
824     if (!interactive_msg(N("Conflicting Packages"), $conflicting_msg, yesno => 1, scroll => 1)) {
825     @nodes_with_deps = ();
826     $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested);
827     goto packages_selection_ok;
828     }
829     }
830    
831     if (my @cant = sort(difference2(\@nodes, \@nodes_with_deps))) {
832 tv 5493 my @ask_unselect = urpm::select::unselected_packages($urpm->{state});
833 dmorgan 535 my @reasons = map {
834     my $cant = $_;
835     my $unsel = find { $_ eq $cant } @ask_unselect;
836     $unsel
837     ? join("\n", urpm::select::translate_why_unselected($urpm, $urpm->{state}, $unsel))
838     : ($pkgs->{$_}{pkg}->flag_skip ? N("%s (belongs to the skip list)", $cant) : $cant);
839     } @cant;
840     my $count = @reasons;
841     interactive_msg(
842 tv 6102 ($count == 1 ? N("One package cannot be installed") : N("Some packages cannot be installed")),
843 dmorgan 535 ($count == 1 ?
844     N("Sorry, the following package cannot be selected:\n\n%s", format_list(@reasons))
845 misc 1124 : N("Sorry, the following packages cannot be selected:\n\n%s", format_list(@reasons))),
846 dmorgan 535 scroll => 1,
847     );
848     foreach (@cant) {
849     next unless $pkgs->{$_}{pkg};
850     $pkgs->{$_}{pkg}->set_flag_requested(0);
851     $pkgs->{$_}{pkg}->set_flag_required(0);
852     }
853     }
854     packages_selection_ok:
855     } else {
856     my @unrequested;
857     slow_func($widget,
858     sub { @unrequested = $urpm->disable_selected(open_rpm_db(), $urpm->{state},
859     map { $pkgs->{$_}{pkg} } @nodes) });
860     @nodes_with_deps = map { urpm_name($_) } @unrequested;
861     statusbar_msg_remove($bar_id);
862     if (!deps_msg(N("Some packages need to be removed"),
863     N("Because of their dependencies, the following package(s) must be unselected now:\n\n"),
864     \@nodes, \@nodes_with_deps)) {
865     @nodes_with_deps = ();
866     $urpm->resolve_requested(open_rpm_db(), $urpm->{state}, { map { $_->id => 1 } @unrequested });
867     goto packages_unselection_ok;
868     }
869     packages_unselection_ok:
870     }
871     }
872    
873     foreach (@nodes_with_deps) {
874     #- some deps may exist on some packages which aren't listed because
875     #- not upgradable (older than what currently installed)
876     exists $pkgs->{$_} or next;
877     if (!$pkgs->{$_}{pkg}) { #- can't be removed # FIXME; what about next packages in the loop?
878 tv 6096 undef $pkgs->{$_}{selected};
879 dmorgan 535 log::explanations("can't be removed: $_");
880     } else {
881     $pkgs->{$_}{selected} = $new_state;
882     }
883     $set_state->($_, node_state($_), $model);
884     if (my $pkg = $pkgs->{$_}{pkg}) {
885     # FIXME: shouldn't we threat all of them as POSITIVE (as selected size)
886     $size_selected += $pkg->size * ($pkg->flag_installed && !$pkg->flag_upgrade ? ($new_state ? -1 : 1) : ($new_state ? 1 : -1));
887     }
888     }
889     }
890    
891     sub is_there_selected_packages() {
892     int(grep { $pkgs->{$_}{selected} } keys %$pkgs);
893     }
894    
895     sub real_quit() {
896     if (is_there_selected_packages()) {
897     interactive_msg(N("Some packages are selected."), N("Some packages are selected.") . "\n" . N("Do you really want to quit?"), yesno => 1) or return;
898     }
899     Gtk2->main_quit;
900     }
901    
902     sub do_action__real {
903     my ($options, $callback_action, $o_info) = @_;
904     require urpm::sys;
905     if (!urpm::sys::check_fs_writable()) {
906     $urpm->{fatal}(1, N("Error: %s appears to be mounted read-only.", $urpm::sys::mountpoint));
907     return 1;
908     }
909     if (!$Rpmdrake::pkg::need_restart && !is_there_selected_packages()) {
910     interactive_msg(N("You need to select some packages first."), N("You need to select some packages first."));
911     return 1;
912     }
913     my $size_added = sum(map { if_($_->flag_selected && !$_->flag_installed, $_->size) } @{$urpm->{depslist}});
914     if ($MODE eq 'install' && $size_free - $size_added/1024 < 50*1024) {
915     interactive_msg(N("Too many packages are selected"),
916 misc 1124 N("Warning: it seems that you are attempting to add so many
917 dmorgan 535 packages that your filesystem may run out of free diskspace,
918     during or after package installation ; this is particularly
919     dangerous and should be considered with care.
920    
921     Do you really want to install all the selected packages?"), yesno => 1)
922     or return 1;
923     }
924     my $res = $callback_action->($urpm, $pkgs);
925     if (!$res) {
926     $force_rebuild = 1;
927 tv 3837 pkgs_provider($options->{tree_mode}, if_($Rpmdrake::pkg::probe_only_for_updates, pure_updates => 1), skip_updating_mu => 1);
928 dmorgan 535 reset_search();
929     $size_selected = 0;
930     (undef, $size_free) = MDK::Common::System::df('/usr');
931     $options->{rebuild_tree}->() if $options->{rebuild_tree};
932     gtktext_insert($o_info, '') if $o_info;
933     }
934     $res;
935     }
936    
937     sub do_action {
938     my ($options, $callback_action, $o_info) = @_;
939     my $res = eval { do_action__real($options, $callback_action, $o_info) };
940     my $err = $@;
941     # FIXME: offer to report the problem into bugzilla:
942     if ($err && $err !~ /cancel_perform/) {
943     interactive_msg(N("Fatal error"),
944     N("A fatal error occurred: %s.", $err));
945     }
946     $res;
947     }
948    
949     sub translate_group {
950     join('/', map { translate($_) } split m|/|, $_[0]);
951     }
952    
953     sub ctreefy {
954     join('|', map { translate($_) } split m|/|, $_[0]);
955     }
956    
957     sub _build_tree {
958     my ($elems, @elems) = @_;
959     #- we populate all the groups tree at first
960     %$elems = ();
961     # better loop on packages, create groups tree and push packages in the proper place:
962     foreach my $pkg (@elems) {
963     my $grp = $pkg->[1];
964 tv 3873 # no state for groups (they're not packages and thus have no state)
965     add_parent($grp, undef);
966 dmorgan 535 $elems->{$grp} ||= [];
967     push @{$elems->{$grp}}, $pkg;
968     }
969     }
970    
971    
972     sub build_tree {
973     my ($tree, $tree_model, $elems, $options, $force_rebuild, $flat, $mode) = @_;
974     state $old_mode;
975     $mode = $options->{rmodes}{$mode} || $mode;
976     return if $old_mode eq $mode && !$force_rebuild;
977     $old_mode = $mode;
978     undef $force_rebuild;
979     my @elems;
980     my $wait; $wait = statusbar_msg(N("Please wait, listing packages...")) if $MODE ne 'update';
981     gtkflush();
982     {
983     my @keys = @filtered_pkgs;
984     if (member($mode, qw(all_updates security bugfix normal))) {
985     @keys = grep {
986     my ($name) = split_fullname($_);
987     member($descriptions->{$name}{importance}, @$mandrakeupdate_wanted_categories)
988     || ! $descriptions->{$name}{importance};
989     } @keys;
990     if (@keys == 0) {
991     add_node('', N("(none)"), { nochild => 1 });
992     state $explanation_only_once;
993     $explanation_only_once or interactive_msg(N("No update"),
994     N("The list of updates is empty. This means that either there is
995     no available update for the packages installed on your computer,
996     or you already installed all of them."));
997     $explanation_only_once = 1;
998     }
999     }
1000 tv 2977 # FIXME: better do this on first group access for faster startup...
1001     @elems = map { [ $_, !$flat && ctreefy($pkgs->{$_}{pkg}->group) ] } sort_packages(@keys);
1002 dmorgan 535 }
1003     my %sortmethods = (
1004     by_size => sub { sort { $pkgs->{$b->[0]}{pkg}->size <=> $pkgs->{$a->[0]}{pkg}->size } @_ },
1005     by_selection => sub { sort { $pkgs->{$b->[0]}{selected} <=> $pkgs->{$a->[0]}{selected}
1006     || uc($a->[0]) cmp uc($b->[0]) } @_ },
1007     by_leaves => sub {
1008     # inlining part of MDK::Common::Data::difference2():
1009     my %l; @l{map { $_->[0] } @_} = ();
1010     my @pkgs_times = ('rpm', '-q', '--qf', '%{name}-%{version}-%{release}.%{arch} %{installtime}\n',
1011     map { chomp_($_) } run_program::get_stdout('urpmi_rpm-find-leaves'));
1012     sort { $b->[1] <=> $a->[1] } grep { exists $l{$_->[0]} } map { chomp; [ split ] } run_rpm(@pkgs_times);
1013     },
1014     flat => sub { no locale; sort { uc($a->[0]) cmp uc($b->[0]) } @_ },
1015     by_medium => sub { sort { $a->[2] <=> $b->[2] || uc($a->[0]) cmp uc($b->[0]) } @_ },
1016     );
1017     if ($flat) {
1018     add_node($_->[0], '') foreach $sortmethods{$::mode->[0] || 'flat'}->(@elems);
1019     } else {
1020     if (0 && $MODE eq 'update') {
1021     add_node($_->[0], N("All")) foreach $sortmethods{flat}->(@elems);
1022     $tree->expand_row($tree_model->get_path($tree_model->get_iter_first), 0);
1023     } elsif ($::mode->[0] eq 'by_source') {
1024     _build_tree($elems, $sortmethods{by_medium}->(map {
1025     my $m = pkg2medium($pkgs->{$_->[0]}{pkg}, $urpm); [ $_->[0], $m->{name}, $m->{priority} ];
1026     } @elems));
1027     } elsif ($::mode->[0] eq 'by_presence') {
1028     _build_tree($elems, map {
1029     my $pkg = $pkgs->{$_->[0]}{pkg};
1030     [ $_->[0], $pkg->flag_installed ?
1031     (!$pkg->flag_skip && $pkg->flag_upgrade ? N("Upgradable") : N("Installed"))
1032     : N("Addable") ];
1033     } $sortmethods{flat}->(@elems));
1034     } else {
1035     _build_tree($elems, @elems);
1036     }
1037     }
1038     statusbar_msg_remove($wait) if defined $wait;
1039     }
1040    
1041     sub get_info {
1042     my ($key, $widget) = @_;
1043     #- the package information hasn't been loaded. Instead of rescanning the media, just give up.
1044     exists $pkgs->{$key} or return [ [ N("Description not available for this package\n") ] ];
1045     #- get the description if needed:
1046 tv 5986 exists $pkgs->{$key}{description} or slow_func($widget, sub { extract_header($pkgs->{$key}, $urpm, 'info', find_installed_version($pkgs->{$key}{pkg})) });
1047 dmorgan 535 format_pkg_simplifiedinfo($pkgs, $key, $urpm, $descriptions);
1048     }
1049    
1050     sub sort_callback {
1051     my ($store, $treeiter1, $treeiter2) = @_;
1052     URPM::rpmvercmp(map { $store->get_value($_, $pkg_columns{version}) } $treeiter1, $treeiter2);
1053     }
1054    
1055     sub run_help_callback {
1056     my (undef, $url) = @_;
1057 tv 2342 my ($user) = grep { $_->[2] eq $ENV{USERHELPER_UID} } list_passwd();
1058     local $ENV{HOME} = $user->[7] if $user && $ENV{USERHELPER_UID};
1059 dmorgan 535 run_program::raw({ detach => 1, as_user => 1 }, 'www-browser', $url);
1060     }
1061    
1062     1;

  ViewVC Help
Powered by ViewVC 1.1.30