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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 535 - (show annotations) (download)
Mon Feb 14 00:37:56 2011 UTC (13 years, 2 months ago) by dmorgan
File size: 42713 byte(s)
Import cleaned rpmdrake
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 Mandriva.");
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 offical update which is supported by Mandriva.")
177 : (N("This is an unoffical update."), $unsupported))
178 ,
179 $s);
180 } else {
181 $s .= N("This is an official package supported by Mandriva") . "\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 dependancy.") 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 { /^$short_name/ } keys %$pkgs),
379 ),
380 scroll => 1,
381 );
382 }
383 return 'XXX';
384 }
385 #- checks $_[0] -> hack for partial tree displaying
386 return 'XXX' if !$_[0];
387 $pkg->{selected} ?
388 ($urpm_obj->flag_installed ?
389 ($urpm_obj->flag_upgrade ? 'to_install' : 'to_remove')
390 : 'to_install')
391 : ($urpm_obj->flag_installed ?
392 ($urpm_obj->flag_upgrade ? 'to_update'
393 : ($urpm_obj->flag_base ? 'base' : 'installed'))
394 : 'uninstalled');
395 }
396
397 my ($common, $w, %wtree, %ptree, %pix);
398
399 sub set_node_state {
400 my ($iter, $state, $model) = @_;
401 ($state eq 'XXX' || !$state) and return;
402 $pix{$state} ||= gtkcreate_pixbuf('state_' . $state);
403 $model->set($iter, $pkg_columns{state_icon} => $pix{$state});
404 $model->set($iter, $pkg_columns{state} => $state);
405 $model->set($iter, $pkg_columns{selected} => to_bool(member($state, qw(base installed to_install)))); #$pkg->{selected}));
406 $model->set($iter, $pkg_columns{selectable} => to_bool($state ne 'base'));
407 }
408
409 sub set_leaf_state {
410 my ($leaf, $state, $model) = @_;
411 set_node_state($_, $state, $model) foreach @{$ptree{$leaf}};
412 }
413
414 sub grep_unselected { grep { exists $pkgs->{$_} && !$pkgs->{$_}{selected} } @_ }
415
416 sub add_parent {
417 my ($root, $state) = @_;
418 $root or return undef;
419 if (my $w = $wtree{$root}) { return $w }
420 my $s; foreach (split '\|', $root) {
421 my $s2 = $s ? "$s|$_" : $_;
422 $wtree{$s2} ||= do {
423 my $pixbuf = get_icon($s2, $s);
424 my $iter = $w->{tree_model}->append_set($s ? add_parent($s, $state, get_icon($s)) : undef,
425 [ $grp_columns{label} => $_, if_($pixbuf, $grp_columns{icon} => $pixbuf) ]);
426 $iter;
427 };
428 $s = $s2;
429 }
430 set_node_state($wtree{$s}, $state, $w->{tree_model}); #- use this state by default as tree is building. #
431 $wtree{$s};
432 }
433
434 sub add_node {
435 my ($leaf, $root, $options) = @_;
436 my $state = node_state($leaf) or return;
437 if ($leaf) {
438 my $iter;
439 if (is_a_package($leaf)) {
440 my ($name, $version, $release, $arch) = split_fullname($leaf);
441 $iter = $w->{detail_list_model}->append_set([ $pkg_columns{text} => $leaf,
442 $pkg_columns{short_name} => format_name_n_summary($name, get_summary($leaf)),
443 $pkg_columns{version} => $version,
444 $pkg_columns{release} => $release,
445 $pkg_columns{arch} => $arch,
446 ]);
447 set_node_state($iter, $state, $w->{detail_list_model});
448 $ptree{$leaf} = [ $iter ];
449 } else {
450 $iter = $w->{tree_model}->append_set(add_parent($root, $state), [ $grp_columns{label} => $leaf ]);
451 push @{$wtree{$leaf}}, $iter;
452 }
453 } else {
454 my $parent = add_parent($root, $state);
455 #- hackery for partial displaying of trees, used in rpmdrake:
456 #- if leaf is void, we may create the parent and one child (to have the [+] in front of the parent in the ctree)
457 #- though we use '' as the label of the child; then rpmdrake will connect on tree_expand, and whenever
458 #- the first child has '' as the label, it will remove the child and add all the "right" children
459 $options->{nochild} or $w->{tree_model}->append_set($parent, [ $grp_columns{label} => '' ]); # test $leaf?
460 }
461 }
462
463 my ($prev_label);
464 sub update_size {
465 my ($common) = shift @_;
466 if ($w->{status}) {
467 my $new_label = $common->{get_status}();
468 $prev_label ne $new_label and $w->{status}->set($prev_label = $new_label);
469 }
470 }
471
472 sub children {
473 my ($w) = @_;
474 map { $w->{detail_list_model}->get($_, $pkg_columns{text}) } gtktreeview_children($w->{detail_list_model});
475 }
476
477 sub toggle_all {
478 my ($common, $_val) = @_;
479 my $w = $common->{widgets};
480 my @l = children($w) or return;
481
482 my @unsel = grep_unselected(@l);
483 my @p = @unsel ?
484 #- not all is selected, select all if no option to potentially override
485 (exists $common->{partialsel_unsel} && $common->{partialsel_unsel}->(\@unsel, \@l) ? difference2(\@l, \@unsel) : @unsel)
486 : @l;
487 toggle_nodes($w->{detail_list}->window, $w->{detail_list_model}, \&set_leaf_state, node_state($p[0]), @p);
488 update_size($common);
489 }
490
491 # ask_browse_tree_given_widgets_for_rpmdrake will run gtk+ loop. its main parameter "common" is a hash containing:
492 # - a "widgets" subhash which holds:
493 # o a "w" reference on a ugtk2 object
494 # o "tree" & "info" references a TreeView
495 # o "info" is a TextView
496 # o "tree_model" is the associated model of "tree"
497 # o "status" references a Label
498 # - some methods: get_info, node_state, build_tree, partialsel_unsel, grep_unselected, rebuild_tree, toggle_nodes, get_status
499 # - "tree_submode": the default mode (by group, ...), ...
500 # - "state": a hash of misc flags: => { flat => '0' },
501 # o "flat": is the tree flat or not
502 # - "tree_mode": mode of the tree ("gui_pkgs", "by_group", ...) (mainly used by rpmdrake)
503
504 sub ask_browse_tree_given_widgets_for_rpmdrake {
505 ($common) = @_;
506 $w = $common->{widgets};
507
508 $w->{detail_list} ||= $w->{tree};
509 $w->{detail_list_model} ||= $w->{tree_model};
510
511 $common->{add_parent} = \&add_parent;
512 my $clear_all_caches = sub {
513 %ptree = %wtree = ();
514 };
515 $common->{clear_all_caches} = $clear_all_caches;
516 $common->{delete_all} = sub {
517 $clear_all_caches->();
518 $w->{detail_list_model}->clear;
519 $w->{tree_model}->clear;
520 };
521 $common->{rebuild_tree} = sub {
522 $common->{delete_all}->();
523 $common->{build_tree}($common->{state}{flat}, $common->{tree_mode});
524 update_size($common);
525 };
526 $common->{delete_category} = sub {
527 my ($cat) = @_;
528 exists $wtree{$cat} or return;
529 %ptree = ();
530
531 if (exists $wtree{$cat}) {
532 my $_iter_str = $w->{tree_model}->get_path_str($wtree{$cat});
533 $w->{tree_model}->remove($wtree{$cat});
534 delete $wtree{$cat};
535 }
536 update_size($common);
537 };
538 $common->{add_nodes} = sub {
539 my (@nodes) = @_;
540 $w->{detail_list_model}->clear;
541 $w->{detail_list}->scroll_to_point(0, 0);
542 add_node($_->[0], $_->[1], $_->[2]) foreach @nodes;
543 update_size($common);
544 };
545
546 $common->{display_info} = sub {
547 gtktext_insert($w->{info}, get_info($_[0], $w->{tree}->window));
548 $w->{info}->scroll_to_iter($w->{info}->get_buffer->get_start_iter, 0, 0, 0, 0);
549 0;
550 };
551
552 my $fast_toggle = sub {
553 my ($iter) = @_;
554 gtkset_mousecursor_wait($w->{w}{rwindow}->window);
555 my $_cleaner = before_leaving { gtkset_mousecursor_normal($w->{w}{rwindow}->window) };
556 my $name = $w->{detail_list_model}->get($iter, $pkg_columns{text});
557 my $urpm_obj = $pkgs->{$name}{pkg};
558
559 if ($urpm_obj->flag_base) {
560 interactive_msg(N("Warning"),
561 N("Removing package %s would break your system", $name));
562 return '';
563 }
564
565 if ($urpm_obj->flag_skip) {
566 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 '';
567 $urpm_obj->set_flag_skip(0);
568 }
569
570 if ($Rpmdrake::pkg::need_restart && !$priority_up_alread_warned) {
571 $priority_up_alread_warned = 1;
572 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");
573 }
574
575 toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $w->{detail_list_model}->get($iter, $pkg_columns{state}),
576 $w->{detail_list_model}->get($iter, $pkg_columns{text}));
577 update_size($common);
578 };
579 $w->{detail_list}->get_selection->signal_connect(changed => sub {
580 my ($model, $iter) = $_[0]->get_selected;
581 $model && $iter or return;
582 $common->{display_info}($model->get($iter, $pkg_columns{text}));
583 });
584 ($w->{detail_list}->get_column(0)->get_cell_renderers)[0]->signal_connect(toggled => sub {
585 my ($_cell, $path) = @_; #text_
586 my $iter = $w->{detail_list_model}->get_iter_from_string($path);
587 $fast_toggle->($iter) if $iter;
588 1;
589 });
590 $common->{rebuild_tree}->();
591 update_size($common);
592 $common->{initial_selection} and toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, undef, @{$common->{initial_selection}});
593 #my $_b = before_leaving { $clear_all_caches->() };
594 $common->{init_callback}->() if $common->{init_callback};
595 $w->{w}->main;
596 }
597
598 our $find_entry;
599
600 sub reset_search() {
601 return if !$common;
602 $common->{delete_category}->($_) foreach $results_ok, $results_none;
603 # clear package list:
604 $common->{add_nodes}->();
605 }
606
607 sub is_a_package {
608 my ($pkg) = @_;
609 return exists $pkgs->{$pkg};
610 }
611
612 sub switch_pkg_list_mode {
613 my ($mode) = @_;
614 return if !$mode;
615 return if !$filter_methods{$mode};
616 $force_displaying_group = 1;
617 $filter_methods{$mode}->();
618 }
619
620 sub is_updatable {
621 my $p = $pkgs->{$_[0]};
622 $p->{pkg} && !$p->{selected} && $p->{pkg}->flag_installed && $p->{pkg}->flag_upgrade;
623 }
624
625 sub pkgs_provider {
626 my ($options, $mode, %options) = @_;
627 return if !$mode;
628 my $h = &get_pkgs($options); # was given (1, @_) for updates
629 ($urpm, $descriptions) = @$h{qw(urpm update_descr)};
630 $pkgs = $h->{all_pkgs};
631 %filters = (
632 non_installed => $h->{installable},
633 installed => $h->{installed},
634 all => [ keys %$pkgs ],
635 );
636 my %tmp_filter_methods = (
637 all => sub {
638 [ difference2([ keys %$pkgs ], $h->{inactive_backports}) ]
639 },
640 all_updates => sub {
641 # potential "updates" from media not tagged as updates:
642 if (!$options{pure_updates} && !$Rpmdrake::pkg::need_restart) {
643 [ @{$h->{updates}},
644 difference2([ grep { is_updatable($_) } @{$h->{installable}} ], $h->{backports}) ];
645 } else {
646 [ difference2($h->{updates}, $h->{inactive_backports}) ];
647 }
648 },
649 backports => sub { $h->{backports} },
650 meta_pkgs => sub {
651 [ difference2($h->{meta_pkgs}, $h->{inactive_backports}) ]
652 },
653 gui_pkgs => sub {
654 [ difference2($h->{gui_pkgs}, $h->{inactive_backports}) ]
655 },
656 );
657 foreach my $importance (qw(bugfix security normal)) {
658 $tmp_filter_methods{$importance} = sub {
659 my @media = keys %$descriptions;
660 [ grep {
661 my ($name) = split_fullname($_);
662 my $medium = find { $descriptions->{$_}{$name} } @media;
663 $medium && $descriptions->{$medium}{$name}{importance} eq $importance } @{$h->{updates}} ];
664 };
665 }
666
667 undef %filter_methods;
668 foreach my $type (keys %tmp_filter_methods) {
669 $filter_methods{$type} = sub {
670 $force_rebuild = 1; # force rebuilding tree since we changed filter (FIXME: switch to SortModel)
671 @filtered_pkgs = intersection($filters{$filter->[0]}, $tmp_filter_methods{$type}->());
672 };
673 }
674
675 switch_pkg_list_mode($mode);
676 }
677
678 sub closure_removal {
679 local $urpm->{state} = {};
680 urpm::select::find_packages_to_remove($urpm, $urpm->{state}, \@_);
681 }
682
683 sub is_locale_available {
684 any { $urpm->{depslist}[$_]->flag_selected } keys %{$urpm->{provides}{$_[0]} || {}} and return 1;
685 my $found;
686 open_rpm_db()->traverse_tag('name', [ $_ ], sub { $found ||= 1 });
687 return $found;
688 }
689
690 sub callback_choices {
691 my (undef, undef, undef, $choices) = @_;
692 return $choices->[0] if $::rpmdrake_options{auto};
693 foreach my $pkg (@$choices) {
694 foreach ($pkg->requires_nosense) {
695 /locales-/ or next;
696 is_locale_available($_) and return $pkg;
697 }
698 }
699 my $callback = sub { interactive_msg(N("More information on package..."), get_info($_[0]), scroll => 1) };
700 $choices = [ sort { $a->name cmp $b->name } @$choices ];
701 my @choices = interactive_list_(N("Please choose"), (scalar(@$choices) == 1 ?
702 N("The following package is needed:") : N("One of the following packages is needed:")),
703 [ map { urpm_name($_) } @$choices ], $callback, nocancel => 1);
704 defined $choices[0] ? $choices->[$choices[0]] : undef;
705 }
706
707 sub deps_msg {
708 return 1 if $dont_show_selections->[0];
709 my ($title, $msg, $nodes, $nodes_with_deps) = @_;
710 my @deps = sort { $a cmp $b } difference2($nodes_with_deps, $nodes);
711 @deps > 0 or return 1;
712 deps_msg_again:
713 my $results = interactive_msg(
714 $title, $msg .
715 format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @deps)
716 . "\n\n" . format_size($urpm->selected_size($urpm->{state})),
717 yesno => [ N("Cancel"), N("More info"), N("Ok") ],
718 scroll => 1,
719 );
720 if ($results eq
721 #-PO: Keep it short, this is gonna be on a button
722 N("More info")) {
723 interactive_packtable(
724 N("Information on packages"),
725 $::main_window,
726 undef,
727 [ map { my $pkg = $_;
728 [ gtknew('HBox', children_tight => [ gtkset_selectable(gtknew('Label', text => $pkg), 1) ]),
729 gtknew('Button', text => N("More information on package..."),
730 clicked => sub {
731 interactive_msg(N("More information on package..."), get_info($pkg), scroll => 1);
732 }) ] } @deps ],
733 [ gtknew('Button', text => N("Ok"),
734 clicked => sub { Gtk2->main_quit }) ]
735 );
736 goto deps_msg_again;
737 } else {
738 return $results eq N("Ok");
739 }
740 }
741
742 sub toggle_nodes {
743 my ($widget, $model, $set_state, $old_state, @nodes) = @_;
744 @nodes = grep { exists $pkgs->{$_} } @nodes
745 or return;
746 #- avoid selecting too many packages at once
747 return if !$dont_show_selections->[0] && @nodes > 2000;
748 my $new_state = !$pkgs->{$nodes[0]}{selected};
749
750 my @nodes_with_deps;
751
752 my $bar_id = statusbar_msg(N("Checking dependencies of package..."), 0);
753
754 my $warn_about_additional_packages_to_remove = sub {
755 my ($msg) = @_;
756 statusbar_msg_remove($bar_id);
757 deps_msg(N("Some additional packages need to be removed"),
758 formatAlaTeX($msg) . "\n\n",
759 \@nodes, \@nodes_with_deps) or @nodes_with_deps = ();
760 };
761
762 if (member($old_state, qw(to_remove installed))) { # remove pacckages
763 if ($new_state) {
764 my @remove;
765 slow_func($widget, sub { @remove = closure_removal(@nodes) });
766 @nodes_with_deps = grep { !$pkgs->{$_}{selected} && !/^basesystem/ } @remove;
767 $warn_about_additional_packages_to_remove->(
768 N("Because of their dependencies, the following package(s) also need to be removed:"));
769 my @impossible_to_remove;
770 foreach (grep { exists $pkgs->{$_}{base} } @remove) {
771 ${$pkgs->{$_}{base}} == 1 ? push @impossible_to_remove, $_ : ${$pkgs->{$_}{base}}--;
772 }
773 @impossible_to_remove and interactive_msg(N("Some packages can't be removed"),
774 N("Removing these packages would break your system, sorry:\n\n") .
775 format_list(@impossible_to_remove));
776 @nodes_with_deps = difference2(\@nodes_with_deps, \@impossible_to_remove);
777 } else {
778 slow_func($widget,
779 sub { @nodes_with_deps = grep { intersection(\@nodes, [ closure_removal($_) ]) }
780 grep { $pkgs->{$_}{selected} && !member($_, @nodes) } keys %$pkgs });
781 push @nodes_with_deps, @nodes;
782 $warn_about_additional_packages_to_remove->(
783 N("Because of their dependencies, the following package(s) must be unselected now:\n\n"));
784 $pkgs->{$_}{base} && ${$pkgs->{$_}{base}}++ foreach @nodes_with_deps;
785 }
786 } else {
787 if ($new_state) {
788 if (@nodes > 1) {
789 #- unselect i18n packages of which locales is not already present (happens when user clicks on KDE group)
790 my @bad_i18n_pkgs;
791 foreach my $sel (@nodes) {
792 foreach ($pkgs->{$sel}{pkg}->requires_nosense) {
793 /locales-([^-]+)/ or next;
794 $sel =~ /-$1[-_]/ && !is_locale_available($_) and push @bad_i18n_pkgs, $sel;
795 }
796 }
797 @nodes = difference2(\@nodes, \@bad_i18n_pkgs);
798 }
799 my @requested;
800 slow_func(
801 $widget,
802 sub {
803 @requested = $urpm->resolve_requested(
804 open_rpm_db(), $urpm->{state},
805 { map { $pkgs->{$_}{pkg}->id => 1 } @nodes },
806 callback_choices => \&callback_choices,
807 );
808 },
809 );
810 @nodes_with_deps = map { urpm_name($_) } @requested;
811 statusbar_msg_remove($bar_id);
812 if (!deps_msg(N("Additional packages needed"),
813 formatAlaTeX(N("To satisfy dependencies, the following package(s) also need to be installed:\n\n")) . "\n\n",
814 \@nodes, \@nodes_with_deps)) {
815 @nodes_with_deps = ();
816 $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested);
817 goto packages_selection_ok;
818 }
819
820 if (my $conflicting_msg = urpm::select::conflicting_packages_msg($urpm, $urpm->{state})) {
821 if (!interactive_msg(N("Conflicting Packages"), $conflicting_msg, yesno => 1, scroll => 1)) {
822 @nodes_with_deps = ();
823 $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested);
824 goto packages_selection_ok;
825 }
826 }
827
828 if (my @cant = sort(difference2(\@nodes, \@nodes_with_deps))) {
829 my @ask_unselect = urpm::select::unselected_packages($urpm, $urpm->{state});
830 my @reasons = map {
831 my $cant = $_;
832 my $unsel = find { $_ eq $cant } @ask_unselect;
833 $unsel
834 ? join("\n", urpm::select::translate_why_unselected($urpm, $urpm->{state}, $unsel))
835 : ($pkgs->{$_}{pkg}->flag_skip ? N("%s (belongs to the skip list)", $cant) : $cant);
836 } @cant;
837 my $count = @reasons;
838 interactive_msg(
839 ($count == 1 ? N("One package cannot be installed") : N("Some packages can't be installed")),
840 ($count == 1 ?
841 N("Sorry, the following package cannot be selected:\n\n%s", format_list(@reasons))
842 : N("Sorry, the following packages can't be selected:\n\n%s", format_list(@reasons))),
843 scroll => 1,
844 );
845 foreach (@cant) {
846 next unless $pkgs->{$_}{pkg};
847 $pkgs->{$_}{pkg}->set_flag_requested(0);
848 $pkgs->{$_}{pkg}->set_flag_required(0);
849 }
850 }
851 packages_selection_ok:
852 } else {
853 my @unrequested;
854 slow_func($widget,
855 sub { @unrequested = $urpm->disable_selected(open_rpm_db(), $urpm->{state},
856 map { $pkgs->{$_}{pkg} } @nodes) });
857 @nodes_with_deps = map { urpm_name($_) } @unrequested;
858 statusbar_msg_remove($bar_id);
859 if (!deps_msg(N("Some packages need to be removed"),
860 N("Because of their dependencies, the following package(s) must be unselected now:\n\n"),
861 \@nodes, \@nodes_with_deps)) {
862 @nodes_with_deps = ();
863 $urpm->resolve_requested(open_rpm_db(), $urpm->{state}, { map { $_->id => 1 } @unrequested });
864 goto packages_unselection_ok;
865 }
866 packages_unselection_ok:
867 }
868 }
869
870 foreach (@nodes_with_deps) {
871 #- some deps may exist on some packages which aren't listed because
872 #- not upgradable (older than what currently installed)
873 exists $pkgs->{$_} or next;
874 if (!$pkgs->{$_}{pkg}) { #- can't be removed # FIXME; what about next packages in the loop?
875 $pkgs->{$_}{selected} = 0;
876 log::explanations("can't be removed: $_");
877 } else {
878 $pkgs->{$_}{selected} = $new_state;
879 }
880 $set_state->($_, node_state($_), $model);
881 if (my $pkg = $pkgs->{$_}{pkg}) {
882 # FIXME: shouldn't we threat all of them as POSITIVE (as selected size)
883 $size_selected += $pkg->size * ($pkg->flag_installed && !$pkg->flag_upgrade ? ($new_state ? -1 : 1) : ($new_state ? 1 : -1));
884 }
885 }
886 }
887
888 sub is_there_selected_packages() {
889 int(grep { $pkgs->{$_}{selected} } keys %$pkgs);
890 }
891
892 sub real_quit() {
893 if (is_there_selected_packages()) {
894 interactive_msg(N("Some packages are selected."), N("Some packages are selected.") . "\n" . N("Do you really want to quit?"), yesno => 1) or return;
895 }
896 Gtk2->main_quit;
897 }
898
899 sub do_action__real {
900 my ($options, $callback_action, $o_info) = @_;
901 require urpm::sys;
902 if (!urpm::sys::check_fs_writable()) {
903 $urpm->{fatal}(1, N("Error: %s appears to be mounted read-only.", $urpm::sys::mountpoint));
904 return 1;
905 }
906 if (!$Rpmdrake::pkg::need_restart && !is_there_selected_packages()) {
907 interactive_msg(N("You need to select some packages first."), N("You need to select some packages first."));
908 return 1;
909 }
910 my $size_added = sum(map { if_($_->flag_selected && !$_->flag_installed, $_->size) } @{$urpm->{depslist}});
911 if ($MODE eq 'install' && $size_free - $size_added/1024 < 50*1024) {
912 interactive_msg(N("Too many packages are selected"),
913 N("Warning: it seems that you are attempting to add so much
914 packages that your filesystem may run out of free diskspace,
915 during or after package installation ; this is particularly
916 dangerous and should be considered with care.
917
918 Do you really want to install all the selected packages?"), yesno => 1)
919 or return 1;
920 }
921 my $res = $callback_action->($urpm, $pkgs);
922 if (!$res) {
923 $force_rebuild = 1;
924 pkgs_provider({ skip_updating_mu => 1 }, $options->{tree_mode}, if_($Rpmdrake::pkg::probe_only_for_updates, pure_updates => 1));
925 reset_search();
926 $size_selected = 0;
927 (undef, $size_free) = MDK::Common::System::df('/usr');
928 $options->{rebuild_tree}->() if $options->{rebuild_tree};
929 gtktext_insert($o_info, '') if $o_info;
930 }
931 $res;
932 }
933
934 sub do_action {
935 my ($options, $callback_action, $o_info) = @_;
936 my $res = eval { do_action__real($options, $callback_action, $o_info) };
937 my $err = $@;
938 # FIXME: offer to report the problem into bugzilla:
939 if ($err && $err !~ /cancel_perform/) {
940 interactive_msg(N("Fatal error"),
941 N("A fatal error occurred: %s.", $err));
942 }
943 $res;
944 }
945
946 sub translate_group {
947 join('/', map { translate($_) } split m|/|, $_[0]);
948 }
949
950 sub ctreefy {
951 join('|', map { translate($_) } split m|/|, $_[0]);
952 }
953
954 sub _build_tree {
955 my ($elems, @elems) = @_;
956 #- we populate all the groups tree at first
957 %$elems = ();
958 # better loop on packages, create groups tree and push packages in the proper place:
959 foreach my $pkg (@elems) {
960 my $grp = $pkg->[1];
961 add_parent($grp);
962 $elems->{$grp} ||= [];
963 push @{$elems->{$grp}}, $pkg;
964 }
965 }
966
967
968 sub build_tree {
969 my ($tree, $tree_model, $elems, $options, $force_rebuild, $flat, $mode) = @_;
970 state $old_mode;
971 $mode = $options->{rmodes}{$mode} || $mode;
972 return if $old_mode eq $mode && !$force_rebuild;
973 $old_mode = $mode;
974 undef $force_rebuild;
975 my @elems;
976 my $wait; $wait = statusbar_msg(N("Please wait, listing packages...")) if $MODE ne 'update';
977 gtkflush();
978 {
979 my @keys = @filtered_pkgs;
980 if (member($mode, qw(all_updates security bugfix normal))) {
981 @keys = grep {
982 my ($name) = split_fullname($_);
983 member($descriptions->{$name}{importance}, @$mandrakeupdate_wanted_categories)
984 || ! $descriptions->{$name}{importance};
985 } @keys;
986 if (@keys == 0) {
987 add_node('', N("(none)"), { nochild => 1 });
988 state $explanation_only_once;
989 $explanation_only_once or interactive_msg(N("No update"),
990 N("The list of updates is empty. This means that either there is
991 no available update for the packages installed on your computer,
992 or you already installed all of them."));
993 $explanation_only_once = 1;
994 }
995 }
996 @elems = map { [ $_, !$flat && ctreefy($pkgs->{$_}{pkg}->group) ] } sort @keys;
997 }
998 my %sortmethods = (
999 by_size => sub { sort { $pkgs->{$b->[0]}{pkg}->size <=> $pkgs->{$a->[0]}{pkg}->size } @_ },
1000 by_selection => sub { sort { $pkgs->{$b->[0]}{selected} <=> $pkgs->{$a->[0]}{selected}
1001 || uc($a->[0]) cmp uc($b->[0]) } @_ },
1002 by_leaves => sub {
1003 # inlining part of MDK::Common::Data::difference2():
1004 my %l; @l{map { $_->[0] } @_} = ();
1005 my @pkgs_times = ('rpm', '-q', '--qf', '%{name}-%{version}-%{release}.%{arch} %{installtime}\n',
1006 map { chomp_($_) } run_program::get_stdout('urpmi_rpm-find-leaves'));
1007 sort { $b->[1] <=> $a->[1] } grep { exists $l{$_->[0]} } map { chomp; [ split ] } run_rpm(@pkgs_times);
1008 },
1009 flat => sub { no locale; sort { uc($a->[0]) cmp uc($b->[0]) } @_ },
1010 by_medium => sub { sort { $a->[2] <=> $b->[2] || uc($a->[0]) cmp uc($b->[0]) } @_ },
1011 );
1012 if ($flat) {
1013 add_node($_->[0], '') foreach $sortmethods{$::mode->[0] || 'flat'}->(@elems);
1014 } else {
1015 if (0 && $MODE eq 'update') {
1016 add_node($_->[0], N("All")) foreach $sortmethods{flat}->(@elems);
1017 $tree->expand_row($tree_model->get_path($tree_model->get_iter_first), 0);
1018 } elsif ($::mode->[0] eq 'by_source') {
1019 _build_tree($elems, $sortmethods{by_medium}->(map {
1020 my $m = pkg2medium($pkgs->{$_->[0]}{pkg}, $urpm); [ $_->[0], $m->{name}, $m->{priority} ];
1021 } @elems));
1022 } elsif ($::mode->[0] eq 'by_presence') {
1023 _build_tree($elems, map {
1024 my $pkg = $pkgs->{$_->[0]}{pkg};
1025 [ $_->[0], $pkg->flag_installed ?
1026 (!$pkg->flag_skip && $pkg->flag_upgrade ? N("Upgradable") : N("Installed"))
1027 : N("Addable") ];
1028 } $sortmethods{flat}->(@elems));
1029 } else {
1030 _build_tree($elems, @elems);
1031 }
1032 }
1033 statusbar_msg_remove($wait) if defined $wait;
1034 }
1035
1036 sub get_info {
1037 my ($key, $widget) = @_;
1038 #- the package information hasn't been loaded. Instead of rescanning the media, just give up.
1039 exists $pkgs->{$key} or return [ [ N("Description not available for this package\n") ] ];
1040 #- get the description if needed:
1041 exists $pkgs->{$key}{description} or slow_func($widget, sub { extract_header($pkgs->{$key}, $urpm, 'info') });
1042 format_pkg_simplifiedinfo($pkgs, $key, $urpm, $descriptions);
1043 }
1044
1045 sub sort_callback {
1046 my ($store, $treeiter1, $treeiter2) = @_;
1047 URPM::rpmvercmp(map { $store->get_value($_, $pkg_columns{version}) } $treeiter1, $treeiter2);
1048 }
1049
1050 sub run_help_callback {
1051 my (undef, $url) = @_;
1052 run_program::raw({ detach => 1, as_user => 1 }, 'www-browser', $url);
1053 }
1054
1055 1;

  ViewVC Help
Powered by ViewVC 1.1.30