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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1116 - (show annotations) (download)
Thu May 5 20:02:56 2011 UTC (10 years, 4 months ago) by dmorgan
File size: 42802 byte(s)
Fix list of matching packages when warning for packages not in list (MDV BZ #62308)
1 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 Rpmdrake::icon;
41 use feature 'state';
42
43 our @EXPORT = qw(
44 $descriptions
45 $find_entry
46 $force_displaying_group
47 $force_rebuild
48 $pkgs
49 $results_ok
50 $results_none
51 $size_free
52 $size_selected
53 $urpm
54 %grp_columns
55 %pkg_columns
56 @filtered_pkgs
57 @initial_selection
58 ask_browse_tree_given_widgets_for_rpmdrake
59 build_tree
60 callback_choices
61 compute_main_window_size
62 do_action
63 get_info
64 get_summary
65 is_locale_available
66 node_state
67 pkgs_provider
68 real_quit
69 reset_search
70 set_node_state
71 sort_callback
72 switch_pkg_list_mode
73 toggle_all
74 toggle_nodes
75 );
76
77 our ($descriptions, %filters, @filtered_pkgs, %filter_methods, $force_displaying_group, $force_rebuild, @initial_selection, $pkgs, $size_free, $size_selected, $urpm);
78 our ($results_ok, $results_none) = (N("Search results"), N("Search results (none)"));
79
80 our %grp_columns = (
81 label => 0,
82 icon => 2,
83 );
84
85 our %pkg_columns = (
86 text => 0,
87 state_icon => 1,
88 state => 2,
89 selected => 3,
90 short_name => 4,
91 version => 5,
92 release => 6,
93 'arch' => 7,
94 selectable => 8,
95 );
96
97
98 sub compute_main_window_size {
99 my ($w) = @_;
100 ($typical_width) = string_size($w->{real_window}, translate("Graphical Environment") . "xmms-more-vis-plugins");
101 $typical_width > 600 and $typical_width = 600; #- try to not being crazy with a too large value
102 $typical_width < 150 and $typical_width = 150;
103 }
104
105 sub get_summary {
106 my ($key) = @_;
107 $pkgs->{$key}{summary} ||= $pkgs->{$key}{pkg}->summary;
108 my $summary = translate($pkgs->{$key}{summary});
109 utf8::valid($summary) ? $summary : ();
110 }
111
112 sub build_expander {
113 my ($pkg, $label, $type, $get_data, $o_installed_version) = @_;
114 my $textview;
115 gtkadd(
116 gtkshow(my $exp = gtksignal_connect(
117 Gtk2::Expander->new(format_field($label)),
118 activate => sub {
119 state $first;
120 return if $first;
121 $first = 1;
122 slow_func($::main_window->window, sub {
123 extract_header($pkg, $urpm, $type, $o_installed_version);
124 gtktext_insert($textview, $get_data->() || [ [ N("(Not available)") ] ]);
125 });
126 })),
127 $textview = gtknew('TextView')
128 );
129 $exp->set_use_markup(1);
130 $exp;
131 }
132
133
134 sub get_advisory_link {
135 my ($update_descr) = @_;
136 my $link = gtkshow(Gtk2::LinkButton->new($update_descr->{URL}, N("Security advisory")));
137 $link->set_uri_hook(\&run_help_callback);
138 [ $link ];
139 }
140
141 sub get_description {
142 my ($pkg, $update_descr) = @_;
143 @{ ugtk2::markup_to_TextView_format(join("\n",
144 (eval {
145 escape_text_for_TextView_markup_format(
146 $pkg->{description}
147 || $update_descr->{description});
148 } || '<i>' . N("No description") . '</i>')
149 )) };
150 }
151
152 sub get_string_from_keywords {
153 my ($medium) = @_;
154 return if !$medium->{mediacfg};
155 my ($distribconf, $medium_name) = @{$medium->{mediacfg}};
156
157 return if !$distribconf;
158
159 my @media_types = split(':', $distribconf->getvalue($medium_name, 'media_type'));
160
161 my $unsupported = N("It is <b>not supported</b> by Mageia.");
162 my $dangerous = N("It may <b>break</b> your system.");
163 my $s;
164 $s .= N("This package is not free software") . "\n" if member('non-free', @media_types);
165 if (member('backport', @media_types)) {
166 return join("\n",
167 N("This package contains a new version that was backported."),
168 $unsupported, $dangerous, $s);
169 } elsif (member('testing', @media_types)) {
170 return join("\n",
171 N("This package is a potential candidate for an update."),
172 $unsupported, $dangerous, $s);
173 } elsif (member('updates', @media_types)) {
174 return join("\n",
175 (member('official', @media_types) ?
176 N("This is an official update which is supported by Mageia.")
177 : (N("This is an unofficial update."), $unsupported))
178 ,
179 $s);
180 } else {
181 $s .= N("This is an official package supported by Mageia") . "\n" if member('official', @media_types);
182 return $s;
183 }
184 }
185
186 sub get_main_text {
187 my ($medium, $name, $summary, $is_update, $update_descr) = @_;
188
189 my $txt = get_string_from_keywords($medium);
190
191 ugtk2::markup_to_TextView_format(
192 # force align "name - summary" to the right with RTL languages (#33603):
193 if_(lang::text_direction_rtl(), "\x{200f}") .
194 join("\n",
195 format_header(join(' - ', $name, $summary)) .
196 # workaround gtk+ bug where GtkTextView wronly limit embedded widget size to bigger line's width (#25533):
197 "\x{200b} \x{feff}" . ' ' x 120,
198 if_($txt, format_field(N("Notice: ")) . $txt),
199 if_($is_update, # is it an update?
200 format_field(N("Importance: ")) . format_update_field($update_descr->{importance}),
201 format_field(N("Reason for update: ")) . format_update_field(rpm_description($update_descr->{pre})),
202 ),
203 '' # extra empty line
204 ));
205 }
206
207 sub get_details {
208 my ($key, $pkg, $upkg, $installed_version, $raw_medium) = @_;
209 my (undef, $version, $release) = split_fullname($key);
210 my $a = ugtk2::markup_to_TextView_format(
211 $spacing . join("\n$spacing",
212 format_field(N("Version: ")) . $version . '-' . $release,
213 ($upkg->flag_installed ?
214 format_field(N("Currently installed version: ")) . $installed_version : ()
215 ),
216 format_field(N("Group: ")) . translate_group($upkg->group),
217 format_field(N("Architecture: ")) . $upkg->arch,
218 format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)),
219 eval { format_field(N("Medium: ")) . $raw_medium->{name} },
220 ),
221 );
222 my @link = get_url_link($upkg, $pkg);
223 push @$a, @link if @link;
224 $a;
225 }
226
227 sub get_new_deps {
228 my ($urpm, $upkg) = @_;
229 my $deps_textview;
230 my @a = [ gtkadd(
231 gtksignal_connect(
232 gtkshow(my $dependencies = Gtk2::Expander->new(format_field(N("New dependencies:")))),
233 activate => sub {
234 slow_func($::main_window->window, sub {
235 my $state = {};
236 my @requested = $urpm->resolve_requested(
237 open_rpm_db(), $state,
238 { $upkg->id => 1 },
239 );
240 $urpm->disable_selected(open_rpm_db(), $state, @requested);
241 my @nodes_with_deps = map { urpm_name($_) } @requested;
242 my @deps = sort { $a cmp $b } difference2(\@nodes_with_deps, [ urpm_name($upkg) ]);
243 @deps = N("No non installed dependency.") if !@deps;
244 gtktext_insert($deps_textview, join("\n", @deps));
245 });
246 }
247 ),
248 $deps_textview = gtknew('TextView')
249 ) ];
250 $dependencies->set_use_markup(1);
251 @a;
252 }
253
254 sub get_url_link {
255 my ($upkg, $pkg) = @_;
256
257 my $url = $upkg->url || $pkg->{url};
258
259 if (!$url) {
260 open_rpm_db()->traverse_tag('name', [ $upkg->name ], sub { $url = $_[0]->url });
261 }
262
263 return if !$url;
264
265 my @a =
266 (@{ ugtk2::markup_to_TextView_format(format_field("\n$spacing" . N("URL: "))) },
267 [ my $link = gtkshow(Gtk2::LinkButton->new($url, $url)) ]);
268 $link->set_uri_hook(\&run_help_callback);
269 @a;
270 }
271
272 sub files_format {
273 my ($files) = @_;
274 ugtk2::markup_to_TextView_format(
275 '<tt>' . $spacing #- to highlight information
276 . join("\n$spacing", map { "\x{200e}$_" } @$files)
277 . '</tt>');
278 }
279
280 sub format_pkg_simplifiedinfo {
281 my ($pkgs, $key, $urpm, $descriptions) = @_;
282 my ($name) = split_fullname($key);
283 my $pkg = $pkgs->{$key};
284 my $upkg = $pkg->{pkg};
285 return if !$upkg;
286 my $raw_medium = pkg2medium($upkg, $urpm);
287 my $medium = !$raw_medium->{fake} ? $raw_medium->{name} : undef;
288 my $update_descr = $descriptions->{$medium}{$name};
289 # discard update fields if not matching:
290 my $is_update = ($upkg->flag_upgrade && $update_descr && $update_descr->{pre});
291 my $summary = get_summary($key);
292 my $s = get_main_text($raw_medium, $name, $summary, $is_update, $update_descr);
293 push @$s, get_advisory_link($update_descr) if $is_update;
294
295 push @$s, get_description($pkg, $update_descr);
296 push @$s, [ "\n" ];
297 my $installed_version = eval { find_installed_version($upkg) };
298
299 push @$s, [ gtkadd(gtkshow(my $details_exp = Gtk2::Expander->new(format_field(N("Details:")))),
300 gtknew('TextView', text => get_details($key, $pkg, $upkg, $installed_version, $raw_medium))) ];
301 $details_exp->set_use_markup(1);
302 push @$s, [ "\n\n" ];
303 push @$s, [ build_expander($pkg, N("Files:"), 'files', sub { files_format($pkg->{files}) }) ];
304 push @$s, [ "\n\n" ];
305 push @$s, [ build_expander($pkg, N("Changelog:"), 'changelog', sub { $pkg->{changelog} }, $installed_version) ];
306
307 push @$s, [ "\n\n" ];
308 if ($upkg->id) { # If not installed
309 push @$s, get_new_deps($urpm, $upkg);
310 }
311 $s;
312 }
313
314 sub format_pkg_info {
315 my ($pkgs, $key, $urpm, $descriptions) = @_;
316 my $pkg = $pkgs->{$key};
317 my $upkg = $pkg->{pkg};
318 my ($name, $version) = split_fullname($key);
319 my @files = (
320 format_field(N("Files:\n")),
321 exists $pkg->{files}
322 ? '<tt>' . join("\n", map { "\x{200e}$_" } @{$pkg->{files}}) . '</tt>' #- to highlight information
323 : N("(Not available)"),
324 );
325 my @chglo = (format_field(N("Changelog:\n")), ($pkg->{changelog} ? @{$pkg->{changelog}} : N("(Not available)")));
326 my @source_info = (
327 $MODE eq 'remove' || !@$max_info_in_descr
328 ? ()
329 : (
330 format_field(N("Medium: ")) . pkg2medium($upkg, $urpm)->{name},
331 format_field(N("Currently installed version: ")) . find_installed_version($upkg),
332 )
333 );
334 my @max_info = @$max_info_in_descr && $changelog_first ? (@chglo, @files) : (@files, '', @chglo);
335 ugtk2::markup_to_TextView_format(join("\n", format_field(N("Name: ")) . $name,
336 format_field(N("Version: ")) . $version,
337 format_field(N("Architecture: ")) . $upkg->arch,
338 format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)),
339 if_(
340 $MODE eq 'update',
341 format_field(N("Importance: ")) . $descriptions->{$name}{importance}
342 ),
343 @source_info,
344 '', # extra empty line
345 format_field(N("Summary: ")) . $pkg->{summary},
346 '', # extra empty line
347 if_(
348 $MODE eq 'update',
349 format_field(N("Reason for update: ")) . rpm_description($descriptions->{$name}{pre}),
350 ),
351 format_field(N("Description: ")), ($pkg->{description} || $descriptions->{$name}{description} || N("No description")),
352 @max_info,
353 ));
354 }
355
356 sub node_state {
357 my ($name) = @_;
358 return if !$name;
359 my $pkg = $pkgs->{$name};
360 my $urpm_obj = $pkg->{pkg};
361 if (!$urpm_obj) {
362 my ($short_name) = split_fullname($name);
363 state $warned;
364 if (!$warned) {
365 $warned = 1;
366 interactive_msg(N("Warning"),
367 join("\n",
368 N("The package \"%s\" was found.", $name),
369 N("However this package is not in the package list."),
370 N("You may want to update your urpmi database."),
371 '',
372 N("Matching packages:"),
373 '',
374 join("\n", sort map {
375 #-PO: this is list fomatting: "- <package_name> (medium: <medium_name>)"
376 #-PO: eg: "- rpmdrake (medium: "Main Release"
377 N("- %s (medium: %s)", $_, pkg2medium($pkgs->{$_}{pkg}, $urpm)->{name});
378 } grep {
379 /^$short_name/ && $pkgs->{$_}{pkg}
380 } keys %$pkgs),
381 ),
382 scroll => 1,
383 );
384 }
385 return 'XXX';
386 }
387 #- checks $_[0] -> hack for partial tree displaying
388 return 'XXX' if !$_[0];
389 $pkg->{selected} ?
390 ($urpm_obj->flag_installed ?
391 ($urpm_obj->flag_upgrade ? 'to_install' : 'to_remove')
392 : 'to_install')
393 : ($urpm_obj->flag_installed ?
394 ($urpm_obj->flag_upgrade ? 'to_update'
395 : ($urpm_obj->flag_base ? 'base' : 'installed'))
396 : 'uninstalled');
397 }
398
399 my ($common, $w, %wtree, %ptree, %pix);
400
401 sub set_node_state {
402 my ($iter, $state, $model) = @_;
403 ($state eq 'XXX' || !$state) and return;
404 $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 my $iter = $w->{tree_model}->append_set($s ? add_parent($s, $state, get_icon($s)) : undef,
427 [ $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 my ($leaf, $root, $options) = @_;
438 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 $options->{nochild} or $w->{tree_model}->append_set($parent, [ $grp_columns{label} => '' ]); # test $leaf?
462 }
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 my ($options, $mode, %options) = @_;
629 return if !$mode;
630 my $h = &get_pkgs($options); # was given (1, @_) for updates
631 ($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 [ difference2([ keys %$pkgs ], $h->{inactive_backports}) ]
641 },
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 [ difference2($h->{meta_pkgs}, $h->{inactive_backports}) ]
654 },
655 gui_pkgs => sub {
656 [ difference2($h->{gui_pkgs}, $h->{inactive_backports}) ]
657 },
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 any { $urpm->{depslist}[$_]->flag_selected } keys %{$urpm->{provides}{$_[0]} || {}} and return 1;
687 my $found;
688 open_rpm_db()->traverse_tag('name', [ $_ ], sub { $found ||= 1 });
689 return $found;
690 }
691
692 sub callback_choices {
693 my (undef, undef, undef, $choices) = @_;
694 return $choices->[0] if $::rpmdrake_options{auto};
695 foreach my $pkg (@$choices) {
696 foreach ($pkg->requires_nosense) {
697 /locales-/ or next;
698 is_locale_available($_) and return $pkg;
699 }
700 }
701 my $callback = sub { interactive_msg(N("More information on package..."), get_info($_[0]), scroll => 1) };
702 $choices = [ sort { $a->name cmp $b->name } @$choices ];
703 my @choices = interactive_list_(N("Please choose"), (scalar(@$choices) == 1 ?
704 N("The following package is needed:") : N("One of the following packages is needed:")),
705 [ map { urpm_name($_) } @$choices ], $callback, nocancel => 1);
706 defined $choices[0] ? $choices->[$choices[0]] : undef;
707 }
708
709 sub deps_msg {
710 return 1 if $dont_show_selections->[0];
711 my ($title, $msg, $nodes, $nodes_with_deps) = @_;
712 my @deps = sort { $a cmp $b } difference2($nodes_with_deps, $nodes);
713 @deps > 0 or return 1;
714 deps_msg_again:
715 my $results = interactive_msg(
716 $title, $msg .
717 format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @deps)
718 . "\n\n" . format_size($urpm->selected_size($urpm->{state})),
719 yesno => [ N("Cancel"), N("More info"), N("Ok") ],
720 scroll => 1,
721 );
722 if ($results eq
723 #-PO: Keep it short, this is gonna be on a button
724 N("More info")) {
725 interactive_packtable(
726 N("Information on packages"),
727 $::main_window,
728 undef,
729 [ map { my $pkg = $_;
730 [ gtknew('HBox', children_tight => [ gtkset_selectable(gtknew('Label', text => $pkg), 1) ]),
731 gtknew('Button', text => N("More information on package..."),
732 clicked => sub {
733 interactive_msg(N("More information on package..."), get_info($pkg), scroll => 1);
734 }) ] } @deps ],
735 [ gtknew('Button', text => N("Ok"),
736 clicked => sub { Gtk2->main_quit }) ]
737 );
738 goto deps_msg_again;
739 } else {
740 return $results eq N("Ok");
741 }
742 }
743
744 sub toggle_nodes {
745 my ($widget, $model, $set_state, $old_state, @nodes) = @_;
746 @nodes = grep { exists $pkgs->{$_} } @nodes
747 or return;
748 #- avoid selecting too many packages at once
749 return if !$dont_show_selections->[0] && @nodes > 2000;
750 my $new_state = !$pkgs->{$nodes[0]}{selected};
751
752 my @nodes_with_deps;
753
754 my $bar_id = statusbar_msg(N("Checking dependencies of package..."), 0);
755
756 my $warn_about_additional_packages_to_remove = sub {
757 my ($msg) = @_;
758 statusbar_msg_remove($bar_id);
759 deps_msg(N("Some additional packages need to be removed"),
760 formatAlaTeX($msg) . "\n\n",
761 \@nodes, \@nodes_with_deps) or @nodes_with_deps = ();
762 };
763
764 if (member($old_state, qw(to_remove installed))) { # remove pacckages
765 if ($new_state) {
766 my @remove;
767 slow_func($widget, sub { @remove = closure_removal(@nodes) });
768 @nodes_with_deps = grep { !$pkgs->{$_}{selected} && !/^basesystem/ } @remove;
769 $warn_about_additional_packages_to_remove->(
770 N("Because of their dependencies, the following package(s) also need to be removed:"));
771 my @impossible_to_remove;
772 foreach (grep { exists $pkgs->{$_}{base} } @remove) {
773 ${$pkgs->{$_}{base}} == 1 ? push @impossible_to_remove, $_ : ${$pkgs->{$_}{base}}--;
774 }
775 @impossible_to_remove and interactive_msg(N("Some packages can't be removed"),
776 N("Removing these packages would break your system, sorry:\n\n") .
777 format_list(@impossible_to_remove));
778 @nodes_with_deps = difference2(\@nodes_with_deps, \@impossible_to_remove);
779 } else {
780 slow_func($widget,
781 sub { @nodes_with_deps = grep { intersection(\@nodes, [ closure_removal($_) ]) }
782 grep { $pkgs->{$_}{selected} && !member($_, @nodes) } keys %$pkgs });
783 push @nodes_with_deps, @nodes;
784 $warn_about_additional_packages_to_remove->(
785 N("Because of their dependencies, the following package(s) must be unselected now:\n\n"));
786 $pkgs->{$_}{base} && ${$pkgs->{$_}{base}}++ foreach @nodes_with_deps;
787 }
788 } else {
789 if ($new_state) {
790 if (@nodes > 1) {
791 #- unselect i18n packages of which locales is not already present (happens when user clicks on KDE group)
792 my @bad_i18n_pkgs;
793 foreach my $sel (@nodes) {
794 foreach ($pkgs->{$sel}{pkg}->requires_nosense) {
795 /locales-([^-]+)/ or next;
796 $sel =~ /-$1[-_]/ && !is_locale_available($_) and push @bad_i18n_pkgs, $sel;
797 }
798 }
799 @nodes = difference2(\@nodes, \@bad_i18n_pkgs);
800 }
801 my @requested;
802 slow_func(
803 $widget,
804 sub {
805 @requested = $urpm->resolve_requested(
806 open_rpm_db(), $urpm->{state},
807 { map { $pkgs->{$_}{pkg}->id => 1 } @nodes },
808 callback_choices => \&callback_choices,
809 );
810 },
811 );
812 @nodes_with_deps = map { urpm_name($_) } @requested;
813 statusbar_msg_remove($bar_id);
814 if (!deps_msg(N("Additional packages needed"),
815 formatAlaTeX(N("To satisfy dependencies, the following package(s) also need to be installed:\n\n")) . "\n\n",
816 \@nodes, \@nodes_with_deps)) {
817 @nodes_with_deps = ();
818 $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested);
819 goto packages_selection_ok;
820 }
821
822 if (my $conflicting_msg = urpm::select::conflicting_packages_msg($urpm, $urpm->{state})) {
823 if (!interactive_msg(N("Conflicting Packages"), $conflicting_msg, yesno => 1, scroll => 1)) {
824 @nodes_with_deps = ();
825 $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested);
826 goto packages_selection_ok;
827 }
828 }
829
830 if (my @cant = sort(difference2(\@nodes, \@nodes_with_deps))) {
831 my @ask_unselect = urpm::select::unselected_packages($urpm, $urpm->{state});
832 my @reasons = map {
833 my $cant = $_;
834 my $unsel = find { $_ eq $cant } @ask_unselect;
835 $unsel
836 ? join("\n", urpm::select::translate_why_unselected($urpm, $urpm->{state}, $unsel))
837 : ($pkgs->{$_}{pkg}->flag_skip ? N("%s (belongs to the skip list)", $cant) : $cant);
838 } @cant;
839 my $count = @reasons;
840 interactive_msg(
841 ($count == 1 ? N("One package cannot be installed") : N("Some packages can't be installed")),
842 ($count == 1 ?
843 N("Sorry, the following package cannot be selected:\n\n%s", format_list(@reasons))
844 : N("Sorry, the following packages can't be selected:\n\n%s", format_list(@reasons))),
845 scroll => 1,
846 );
847 foreach (@cant) {
848 next unless $pkgs->{$_}{pkg};
849 $pkgs->{$_}{pkg}->set_flag_requested(0);
850 $pkgs->{$_}{pkg}->set_flag_required(0);
851 }
852 }
853 packages_selection_ok:
854 } else {
855 my @unrequested;
856 slow_func($widget,
857 sub { @unrequested = $urpm->disable_selected(open_rpm_db(), $urpm->{state},
858 map { $pkgs->{$_}{pkg} } @nodes) });
859 @nodes_with_deps = map { urpm_name($_) } @unrequested;
860 statusbar_msg_remove($bar_id);
861 if (!deps_msg(N("Some packages need to be removed"),
862 N("Because of their dependencies, the following package(s) must be unselected now:\n\n"),
863 \@nodes, \@nodes_with_deps)) {
864 @nodes_with_deps = ();
865 $urpm->resolve_requested(open_rpm_db(), $urpm->{state}, { map { $_->id => 1 } @unrequested });
866 goto packages_unselection_ok;
867 }
868 packages_unselection_ok:
869 }
870 }
871
872 foreach (@nodes_with_deps) {
873 #- some deps may exist on some packages which aren't listed because
874 #- not upgradable (older than what currently installed)
875 exists $pkgs->{$_} or next;
876 if (!$pkgs->{$_}{pkg}) { #- can't be removed # FIXME; what about next packages in the loop?
877 $pkgs->{$_}{selected} = 0;
878 log::explanations("can't be removed: $_");
879 } else {
880 $pkgs->{$_}{selected} = $new_state;
881 }
882 $set_state->($_, node_state($_), $model);
883 if (my $pkg = $pkgs->{$_}{pkg}) {
884 # FIXME: shouldn't we threat all of them as POSITIVE (as selected size)
885 $size_selected += $pkg->size * ($pkg->flag_installed && !$pkg->flag_upgrade ? ($new_state ? -1 : 1) : ($new_state ? 1 : -1));
886 }
887 }
888 }
889
890 sub is_there_selected_packages() {
891 int(grep { $pkgs->{$_}{selected} } keys %$pkgs);
892 }
893
894 sub real_quit() {
895 if (is_there_selected_packages()) {
896 interactive_msg(N("Some packages are selected."), N("Some packages are selected.") . "\n" . N("Do you really want to quit?"), yesno => 1) or return;
897 }
898 Gtk2->main_quit;
899 }
900
901 sub do_action__real {
902 my ($options, $callback_action, $o_info) = @_;
903 require urpm::sys;
904 if (!urpm::sys::check_fs_writable()) {
905 $urpm->{fatal}(1, N("Error: %s appears to be mounted read-only.", $urpm::sys::mountpoint));
906 return 1;
907 }
908 if (!$Rpmdrake::pkg::need_restart && !is_there_selected_packages()) {
909 interactive_msg(N("You need to select some packages first."), N("You need to select some packages first."));
910 return 1;
911 }
912 my $size_added = sum(map { if_($_->flag_selected && !$_->flag_installed, $_->size) } @{$urpm->{depslist}});
913 if ($MODE eq 'install' && $size_free - $size_added/1024 < 50*1024) {
914 interactive_msg(N("Too many packages are selected"),
915 N("Warning: it seems that you are attempting to add so much
916 packages that your filesystem may run out of free diskspace,
917 during or after package installation ; this is particularly
918 dangerous and should be considered with care.
919
920 Do you really want to install all the selected packages?"), yesno => 1)
921 or return 1;
922 }
923 my $res = $callback_action->($urpm, $pkgs);
924 if (!$res) {
925 $force_rebuild = 1;
926 pkgs_provider({ skip_updating_mu => 1 }, $options->{tree_mode}, if_($Rpmdrake::pkg::probe_only_for_updates, pure_updates => 1));
927 reset_search();
928 $size_selected = 0;
929 (undef, $size_free) = MDK::Common::System::df('/usr');
930 $options->{rebuild_tree}->() if $options->{rebuild_tree};
931 gtktext_insert($o_info, '') if $o_info;
932 }
933 $res;
934 }
935
936 sub do_action {
937 my ($options, $callback_action, $o_info) = @_;
938 my $res = eval { do_action__real($options, $callback_action, $o_info) };
939 my $err = $@;
940 # FIXME: offer to report the problem into bugzilla:
941 if ($err && $err !~ /cancel_perform/) {
942 interactive_msg(N("Fatal error"),
943 N("A fatal error occurred: %s.", $err));
944 }
945 $res;
946 }
947
948 sub translate_group {
949 join('/', map { translate($_) } split m|/|, $_[0]);
950 }
951
952 sub ctreefy {
953 join('|', map { translate($_) } split m|/|, $_[0]);
954 }
955
956 sub _build_tree {
957 my ($elems, @elems) = @_;
958 #- we populate all the groups tree at first
959 %$elems = ();
960 # better loop on packages, create groups tree and push packages in the proper place:
961 foreach my $pkg (@elems) {
962 my $grp = $pkg->[1];
963 add_parent($grp);
964 $elems->{$grp} ||= [];
965 push @{$elems->{$grp}}, $pkg;
966 }
967 }
968
969
970 sub build_tree {
971 my ($tree, $tree_model, $elems, $options, $force_rebuild, $flat, $mode) = @_;
972 state $old_mode;
973 $mode = $options->{rmodes}{$mode} || $mode;
974 return if $old_mode eq $mode && !$force_rebuild;
975 $old_mode = $mode;
976 undef $force_rebuild;
977 my @elems;
978 my $wait; $wait = statusbar_msg(N("Please wait, listing packages...")) if $MODE ne 'update';
979 gtkflush();
980 {
981 my @keys = @filtered_pkgs;
982 if (member($mode, qw(all_updates security bugfix normal))) {
983 @keys = grep {
984 my ($name) = split_fullname($_);
985 member($descriptions->{$name}{importance}, @$mandrakeupdate_wanted_categories)
986 || ! $descriptions->{$name}{importance};
987 } @keys;
988 if (@keys == 0) {
989 add_node('', N("(none)"), { nochild => 1 });
990 state $explanation_only_once;
991 $explanation_only_once or interactive_msg(N("No update"),
992 N("The list of updates is empty. This means that either there is
993 no available update for the packages installed on your computer,
994 or you already installed all of them."));
995 $explanation_only_once = 1;
996 }
997 }
998 @elems = map { [ $_, !$flat && ctreefy($pkgs->{$_}{pkg}->group) ] } sort @keys;
999 }
1000 my %sortmethods = (
1001 by_size => sub { sort { $pkgs->{$b->[0]}{pkg}->size <=> $pkgs->{$a->[0]}{pkg}->size } @_ },
1002 by_selection => sub { sort { $pkgs->{$b->[0]}{selected} <=> $pkgs->{$a->[0]}{selected}
1003 || uc($a->[0]) cmp uc($b->[0]) } @_ },
1004 by_leaves => sub {
1005 # inlining part of MDK::Common::Data::difference2():
1006 my %l; @l{map { $_->[0] } @_} = ();
1007 my @pkgs_times = ('rpm', '-q', '--qf', '%{name}-%{version}-%{release}.%{arch} %{installtime}\n',
1008 map { chomp_($_) } run_program::get_stdout('urpmi_rpm-find-leaves'));
1009 sort { $b->[1] <=> $a->[1] } grep { exists $l{$_->[0]} } map { chomp; [ split ] } run_rpm(@pkgs_times);
1010 },
1011 flat => sub { no locale; sort { uc($a->[0]) cmp uc($b->[0]) } @_ },
1012 by_medium => sub { sort { $a->[2] <=> $b->[2] || uc($a->[0]) cmp uc($b->[0]) } @_ },
1013 );
1014 if ($flat) {
1015 add_node($_->[0], '') foreach $sortmethods{$::mode->[0] || 'flat'}->(@elems);
1016 } else {
1017 if (0 && $MODE eq 'update') {
1018 add_node($_->[0], N("All")) foreach $sortmethods{flat}->(@elems);
1019 $tree->expand_row($tree_model->get_path($tree_model->get_iter_first), 0);
1020 } elsif ($::mode->[0] eq 'by_source') {
1021 _build_tree($elems, $sortmethods{by_medium}->(map {
1022 my $m = pkg2medium($pkgs->{$_->[0]}{pkg}, $urpm); [ $_->[0], $m->{name}, $m->{priority} ];
1023 } @elems));
1024 } elsif ($::mode->[0] eq 'by_presence') {
1025 _build_tree($elems, map {
1026 my $pkg = $pkgs->{$_->[0]}{pkg};
1027 [ $_->[0], $pkg->flag_installed ?
1028 (!$pkg->flag_skip && $pkg->flag_upgrade ? N("Upgradable") : N("Installed"))
1029 : N("Addable") ];
1030 } $sortmethods{flat}->(@elems));
1031 } else {
1032 _build_tree($elems, @elems);
1033 }
1034 }
1035 statusbar_msg_remove($wait) if defined $wait;
1036 }
1037
1038 sub get_info {
1039 my ($key, $widget) = @_;
1040 #- the package information hasn't been loaded. Instead of rescanning the media, just give up.
1041 exists $pkgs->{$key} or return [ [ N("Description not available for this package\n") ] ];
1042 #- get the description if needed:
1043 exists $pkgs->{$key}{description} or slow_func($widget, sub { extract_header($pkgs->{$key}, $urpm, 'info') });
1044 format_pkg_simplifiedinfo($pkgs, $key, $urpm, $descriptions);
1045 }
1046
1047 sub sort_callback {
1048 my ($store, $treeiter1, $treeiter2) = @_;
1049 URPM::rpmvercmp(map { $store->get_value($_, $pkg_columns{version}) } $treeiter1, $treeiter2);
1050 }
1051
1052 sub run_help_callback {
1053 my (undef, $url) = @_;
1054 run_program::raw({ detach => 1, as_user => 1 }, 'www-browser', $url);
1055 }
1056
1057 1;

  ViewVC Help
Powered by ViewVC 1.1.28