1 |
#!/usr/bin/perl |
2 |
# -*- coding: utf-8 -*- |
3 |
#***************************************************************************** |
4 |
# |
5 |
# Copyright (c) 2002 Guillaume Cottenceau |
6 |
# Copyright (c) 2002-2008 Thierry Vignaud <tvignaud@mandriva.com> |
7 |
# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA |
8 |
# Copyright (c) 2005-2008 Mandriva SA |
9 |
# |
10 |
# This program is free software; you can redistribute it and/or modify |
11 |
# it under the terms of the GNU General Public License version 2, as |
12 |
# published by the Free Software Foundation. |
13 |
# |
14 |
# This program is distributed in the hope that it will be useful, |
15 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
# GNU General Public License for more details. |
18 |
# |
19 |
# You should have received a copy of the GNU General Public License |
20 |
# along with this program; if not, write to the Free Software |
21 |
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 |
# |
23 |
#***************************************************************************** |
24 |
# |
25 |
# $Id: rpmdrake 267936 2010-04-26 16:40:21Z jvictor $ |
26 |
|
27 |
use strict; |
28 |
use MDK::Common::Func 'any'; |
29 |
use lib qw(/usr/lib/libDrakX); |
30 |
use common; |
31 |
use utf8; |
32 |
|
33 |
use Rpmdrake::init; |
34 |
use standalone; #- standalone must be loaded very first, for 'explanations', but after rpmdrake::init |
35 |
use rpmdrake; |
36 |
use Rpmdrake::open_db; |
37 |
use Rpmdrake::gui; |
38 |
use Rpmdrake::rpmnew; |
39 |
use Rpmdrake::formatting; |
40 |
use Rpmdrake::pkg; |
41 |
use urpm::media; |
42 |
|
43 |
use mygtk2 qw(gtknew); #- do not import anything else, especially gtkadd() which conflicts with ugtk2 one |
44 |
use ugtk2 qw(:all); |
45 |
use Gtk2::Gdk::Keysyms; |
46 |
use Rpmdrake::widgets; |
47 |
use feature 'state'; |
48 |
|
49 |
$ugtk2::wm_icon = get_icon('installremoverpm', "title-$MODE"); |
50 |
|
51 |
our $w; |
52 |
our $statusbar; |
53 |
|
54 |
my %elems; |
55 |
|
56 |
sub do_search($$$$$$$) { |
57 |
my ($find_entry, $tree, $tree_model, $options, $current_search_type, $urpm, $pkgs) = @_; |
58 |
my $entry = $find_entry->get_text or return; |
59 |
if (!$use_regexp->[0]) { |
60 |
$entry = quotemeta $entry; |
61 |
# enable OR search by default: |
62 |
$entry =~ s/\\ /|/g if $current_search_type eq 'normal'; |
63 |
} |
64 |
# remove leading/trailing spacing when pasting: |
65 |
if ($entry !~ /\S\s\S/) { |
66 |
# if spacing in middle, likely a string search in description |
67 |
$entry =~ s/^\s*//; |
68 |
$entry =~ s/^\s*$//; |
69 |
} |
70 |
my $entry_rx = eval { qr/$entry/i } or return; |
71 |
reset_search(); |
72 |
$options->{state}{flat} and $options->{delete_all}->(); |
73 |
$tree->collapse_all; |
74 |
my @search_results; |
75 |
if ($current_search_type eq 'normal') { |
76 |
my $count; |
77 |
foreach (@filtered_pkgs) { |
78 |
if ($NVR_searches->[0]) { |
79 |
next if !/$entry_rx/; |
80 |
} else { |
81 |
next if first(split_fullname($_)) !~ /$entry_rx/; |
82 |
} |
83 |
push @search_results, $_; |
84 |
# FIXME: should be done for all research types |
85 |
last if $count++ > 2000; |
86 |
} |
87 |
} elsif ($current_search_type eq 'summaries') { |
88 |
my $count; |
89 |
foreach (@filtered_pkgs) { |
90 |
next if get_summary($_) !~ /$entry_rx/; |
91 |
push @search_results, $_; |
92 |
# FIXME: should be done for all research types |
93 |
last if $count++ > 2000; |
94 |
} |
95 |
} else { |
96 |
my $searchstop; |
97 |
my $searchw = ugtk2->new(N("Software Management"), grab => 1, transient => $w->{real_window}); |
98 |
gtkadd( |
99 |
$searchw->{window}, |
100 |
gtkpack__( |
101 |
gtknew('VBox', spacing => 5), |
102 |
gtknew('Label', text => N("Please wait, searching...")), |
103 |
my $searchprogress = gtknew('ProgressBar', width => 300), |
104 |
gtkpack__( |
105 |
gtknew('HButtonBox', layout => 'spread'), |
106 |
gtksignal_connect( |
107 |
Gtk2::Button->new(but(N("Stop"))), |
108 |
clicked => sub { $searchstop = 1 }, |
109 |
), |
110 |
), |
111 |
), |
112 |
); |
113 |
$searchw->sync; |
114 |
# should probably not account backports packages or find a way to search them: |
115 |
my $total_size = keys %$pkgs; |
116 |
my $progresscount; |
117 |
|
118 |
my $update_search_pb = sub { |
119 |
$progresscount++; |
120 |
if (!($progresscount % 100)) { |
121 |
$progresscount <= $total_size and $searchprogress->set_fraction($progresscount/$total_size); |
122 |
$searchw->flush; # refresh and handle clicks |
123 |
} |
124 |
}; |
125 |
foreach my $medium (grep { !$_->{ignore} } @{$urpm->{media}}) { |
126 |
$searchstop and last; |
127 |
my $gurpm; # per medium download progress bar (if needed) |
128 |
my $_gurpm_clean_guard = before_leaving { undef $gurpm }; |
129 |
my $xml_info_file = |
130 |
urpm::media::any_xml_info($urpm, $medium, |
131 |
($current_search_type eq 'files' ? 'files' : 'info'), |
132 |
undef, |
133 |
sub { |
134 |
$gurpm ||= Rpmdrake::gurpm->new(N("Please wait"), |
135 |
transient => $::main_window); |
136 |
download_callback($gurpm, @_) or do { |
137 |
$searchstop = 1; |
138 |
}; |
139 |
}); |
140 |
if (!$xml_info_file) { |
141 |
$urpm->{error}(N("no xml-info available for medium \"%s\"", $medium->{name})); |
142 |
next; |
143 |
} |
144 |
$searchstop and last; |
145 |
|
146 |
require urpm::xml_info; |
147 |
require urpm::xml_info_pkg; |
148 |
|
149 |
$urpm->{log}("getting information from $xml_info_file"); |
150 |
if ($current_search_type eq 'files') { |
151 |
# special version for speed (3x faster), hopefully fully compatible |
152 |
my $F = urpm::xml_info::open_lzma($xml_info_file); |
153 |
my $fn; |
154 |
local $_; |
155 |
while (<$F>) { |
156 |
if ($searchstop) { |
157 |
statusbar_msg(N("Search aborted"), 1); |
158 |
goto end_search; |
159 |
} |
160 |
if (m!^<!) { |
161 |
($fn) = /fn="(.*)"/; |
162 |
$update_search_pb->(); |
163 |
} elsif (/$entry_rx/) { |
164 |
$fn or $urpm->{fatal}("fast algorithm is broken, please report a bug"); |
165 |
push @search_results, $fn; |
166 |
} |
167 |
} |
168 |
} else { |
169 |
eval { |
170 |
urpm::xml_info::do_something_with_nodes( |
171 |
'info', |
172 |
$xml_info_file, |
173 |
sub { |
174 |
$searchstop and die 'search aborted'; |
175 |
my ($node) = @_; |
176 |
$update_search_pb->(); |
177 |
push @search_results, $node->{fn} if $node->{description} =~ $entry_rx; |
178 |
#$searchstop and last; |
179 |
return 0 || $searchstop; |
180 |
}, |
181 |
); |
182 |
}; |
183 |
my $err = $@; |
184 |
if ($err =~ /search aborted/) { |
185 |
statusbar_msg(N("Search aborted"), 1); |
186 |
} |
187 |
} |
188 |
} |
189 |
|
190 |
end_search: |
191 |
@search_results = uniq(@search_results); #- there can be multiple packages with same version/release for different arch's |
192 |
@search_results = intersection(\@search_results, \@filtered_pkgs); |
193 |
$searchw->destroy; |
194 |
} |
195 |
|
196 |
my $iter; |
197 |
if (@search_results) { |
198 |
$elems{$results_ok} = [ map { [ $_, $results_ok ] } sort { uc($a) cmp uc($b) } @search_results ]; |
199 |
$iter = $options->{add_parent}->($results_ok); |
200 |
$options->{add_nodes}->(map { [ $_, $results_ok . ($options->{tree_mode} eq 'by_presence' |
201 |
? '|' . ($pkgs->{$_}{pkg}->flag_installed ? N("Upgradable") : N("Addable")) |
202 |
: ($options->{tree_mode} eq 'by_selection' |
203 |
? '|' . ($pkgs->{$_}{selected} ? N("Selected") : N("Not selected")) |
204 |
: '')) |
205 |
] } sort { uc($a) cmp uc($b) } @search_results); |
206 |
} else { |
207 |
$iter = $options->{add_parent}->($results_none); |
208 |
# clear package list: |
209 |
$options->{add_nodes}->(); |
210 |
my $string = $default_list_mode eq 'all' && $filter->[0] eq 'all' ? N("No search results.") : |
211 |
N("No search results. You may want to switch to the '%s' view and to the '%s' filter", |
212 |
N("All"), N("All"),); |
213 |
statusbar_msg($string , 1); |
214 |
gtkset_mousecursor_normal($::w->{rwindow}->window); |
215 |
} |
216 |
my $tree_selection = $tree->get_selection; |
217 |
if (my $path = $tree_model->get_path($iter)) { |
218 |
$tree_selection->select_path($path); |
219 |
$tree->scroll_to_cell($path, undef, 1, 0.5, 0); |
220 |
$tree_selection->signal_emit('changed'); |
221 |
} |
222 |
} |
223 |
|
224 |
sub quit() { |
225 |
($rpmdrake_width->[0], $rpmdrake_height->[0]) = $::w->{real_window}->get_size(); |
226 |
real_quit(); |
227 |
} |
228 |
|
229 |
sub run_treeview_dialog { |
230 |
my ($callback_action) = @_; |
231 |
|
232 |
my ($options, $tree, $tree_model, $detail_list, $detail_list_model); |
233 |
(undef, $size_free) = MDK::Common::System::df('/usr'); |
234 |
|
235 |
$::main_window = $w->{real_window}; |
236 |
|
237 |
$options = { |
238 |
build_tree => sub { build_tree($tree, $tree_model, \%elems, $options, $force_rebuild, @_) }, |
239 |
partialsel_unsel => sub { |
240 |
my ($unsel, $sel) = @_; |
241 |
@$sel = grep { exists $pkgs->{$_} } @$sel; |
242 |
@$unsel < @$sel; |
243 |
}, |
244 |
get_status => sub { |
245 |
N("Selected: %s / Free disk space: %s", formatXiB($size_selected), formatXiB($size_free*1024)); |
246 |
}, |
247 |
rebuild_tree => sub {}, |
248 |
}; |
249 |
|
250 |
$tree_model = Gtk2::TreeStore->new("Glib::String", "Glib::String", "Gtk2::Gdk::Pixbuf"); |
251 |
$tree_model->set_sort_column_id($grp_columns{label}, 'ascending'); |
252 |
$tree = Gtk2::TreeView->new_with_model($tree_model); |
253 |
$tree->get_selection->set_mode('browse'); |
254 |
|
255 |
$tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::MDV::CellRendererPixWithLabel->new, 'pixbuf' => $grp_columns{icon}, label => $grp_columns{label})); |
256 |
$tree->set_headers_visible(0); |
257 |
|
258 |
$detail_list_model = Gtk2::ListStore->new("Glib::String", |
259 |
"Gtk2::Gdk::Pixbuf", |
260 |
"Glib::String", |
261 |
"Glib::Boolean", |
262 |
"Glib::String", |
263 |
"Glib::String", |
264 |
"Glib::String", |
265 |
"Glib::String", |
266 |
"Glib::Boolean"); |
267 |
|
268 |
$detail_list = Gtk2::TreeView->new_with_model($detail_list_model); |
269 |
$detail_list->append_column( |
270 |
my $col_sel = Gtk2::TreeViewColumn->new_with_attributes( |
271 |
undef, |
272 |
Gtk2::CellRendererToggle->new, |
273 |
active => $pkg_columns{selected}, |
274 |
activatable => $pkg_columns{selectable} |
275 |
)); |
276 |
$col_sel->set_fixed_width(34); # w/o this the toggle cells are not displayed |
277 |
$col_sel->set_sizing('fixed'); |
278 |
$col_sel->set_sort_column_id($pkg_columns{selected}); |
279 |
|
280 |
my $display_arch_col = to_bool(arch() =~ /64/); |
281 |
my @columns = (qw(name version release), if_($display_arch_col, 'arch')); |
282 |
|
283 |
my %columns = ( |
284 |
'name' => { |
285 |
title => N("Package"), |
286 |
markup => $pkg_columns{short_name}, |
287 |
}, |
288 |
'version' => { |
289 |
title => N("Version"), |
290 |
text => $pkg_columns{version}, |
291 |
}, |
292 |
'release' => { |
293 |
title => N("Release"), |
294 |
text => $pkg_columns{release}, |
295 |
}, |
296 |
if_($display_arch_col, 'arch' => { |
297 |
title => |
298 |
#-PO: "Architecture" but to be kept *small* !!! |
299 |
N("Arch."), |
300 |
text => $pkg_columns{arch}, |
301 |
}), |
302 |
); |
303 |
foreach my $col (@columns{@columns}) { |
304 |
$detail_list->append_column( |
305 |
$col->{widget} = |
306 |
Gtk2::TreeViewColumn->new_with_attributes( |
307 |
' ' . $col->{title} . ' ', |
308 |
$col->{renderer} = Gtk2::CellRendererText->new, |
309 |
($col->{markup} ? (markup => $col->{markup}) : (text => $col->{text})), |
310 |
) |
311 |
); |
312 |
$col->{widget}->set_sort_column_id($col->{markup} || $col->{text}); |
313 |
} |
314 |
$columns{$_}{widget}->set_sizing('autosize') foreach @columns; |
315 |
$columns{name}{widget}->set_property('expand', '1'); |
316 |
$columns{name}{renderer}->set_property('ellipsize', 'end'); |
317 |
$columns{$_}{renderer}->set_property('xpad', '6') foreach @columns; |
318 |
$columns{name}{widget}->set_resizable(1); |
319 |
#$detail_list_model->set_sort_column_id($pkg_columns{text}, 'ascending'); |
320 |
$detail_list_model->set_sort_func($pkg_columns{version}, \&sort_callback); |
321 |
$detail_list->set_rules_hint(1); |
322 |
|
323 |
$detail_list->append_column( |
324 |
my $pixcolumn = |
325 |
Gtk2::TreeViewColumn->new_with_attributes( |
326 |
#-PO: "Status" should be kept *small* !!! |
327 |
N("Status"), |
328 |
my $rdr = Gtk2::CellRendererPixbuf->new, |
329 |
'pixbuf' => $pkg_columns{state_icon}) |
330 |
); |
331 |
$rdr->set_fixed_size(34, 24); |
332 |
$pixcolumn->set_sort_column_id($pkg_columns{state}); |
333 |
|
334 |
compute_main_window_size($w); |
335 |
|
336 |
my $cursor_to_restore; |
337 |
$_->signal_connect( |
338 |
expose_event => sub { |
339 |
$cursor_to_restore or return; |
340 |
gtkset_mousecursor_normal($tree->window); |
341 |
undef $cursor_to_restore; |
342 |
}, |
343 |
) foreach $tree, $detail_list; |
344 |
$tree->get_selection->signal_connect(changed => sub { |
345 |
my ($model, $iter) = $_[0]->get_selected; |
346 |
return if !$iter; |
347 |
state $current_group; |
348 |
my $new_group = $model->get_path_str($iter); |
349 |
return if $current_group eq $new_group && !$force_displaying_group; |
350 |
undef $force_displaying_group; |
351 |
$current_group = $new_group; |
352 |
$model && $iter or return; |
353 |
my $group = $model->get($iter, 0); |
354 |
my $parent = $iter; |
355 |
while ($parent = $model->iter_parent($parent)) { |
356 |
$group = join('|', $model->get($parent, 0), $group); |
357 |
} |
358 |
$detail_list->window->freeze_updates; |
359 |
$options->{add_nodes}->(@{$elems{$group}}); |
360 |
$detail_list->window->thaw_updates if $detail_list->window; |
361 |
}); |
362 |
|
363 |
$options->{state}{splited} = 1; |
364 |
$options->{state}{flat} = $tree_flat->[0]; |
365 |
|
366 |
my $is_backports = get_inactive_backport_media(fast_open_urpmi_db()); |
367 |
|
368 |
my %filters = (all => N("All"), |
369 |
installed => N("Installed"), |
370 |
non_installed => N("Not installed"), |
371 |
); |
372 |
|
373 |
my %rfilters = reverse %filters; |
374 |
|
375 |
|
376 |
# handle migrating config file from rpmdrake <= 4.9 |
377 |
if (exists $filters{$default_list_mode}) { |
378 |
$filter->[0] = $default_list_mode; |
379 |
$default_list_mode = 'all'; |
380 |
} |
381 |
|
382 |
$options->{tree_mode} = $default_list_mode; |
383 |
|
384 |
my %modes = ( |
385 |
flat => N("All packages, alphabetical"), |
386 |
by_group => N("All packages, by group"), |
387 |
by_leaves => N("Leaves only, sorted by install date"), |
388 |
by_presence => N("All packages, by update availability"), |
389 |
by_selection => N("All packages, by selection state"), |
390 |
by_size => N("All packages, by size"), |
391 |
by_source => N("All packages, by medium repository"), |
392 |
); |
393 |
|
394 |
|
395 |
my %views = (all => N("All"), |
396 |
if_($is_backports, backports => |
397 |
#-PO: Backports media are newer but less-tested versions of some packages in main |
398 |
#-PO: See http://wiki.mandriva.com/en/Policies/SoftwareMedia#.2Fmain.2Fbackports |
399 |
N("Backports")), |
400 |
meta_pkgs => N("Meta packages"), |
401 |
gui_pkgs => N("Packages with GUI"), |
402 |
all_updates => N("All updates"), |
403 |
security => N("Security updates"), |
404 |
bugfix => N("Bugfixes updates"), |
405 |
normal => N("General updates") |
406 |
); |
407 |
my %rviews = reverse %views; |
408 |
$options->{rviews} = \%rviews; |
409 |
|
410 |
my %default_mode = (install => 'all', # we want the new GUI by default instead of "non_installed" |
411 |
remove => 'installed', |
412 |
update => 'security', |
413 |
); |
414 |
my %wanted_categories = ( |
415 |
all_updates => [ qw(security bugfix normal) ], |
416 |
security => [ 'security' ], |
417 |
bugfix => [ 'bugfix' ], |
418 |
normal => [ 'normal' ], |
419 |
); |
420 |
my $old_value; |
421 |
my $view_box = gtknew( |
422 |
'ComboBox', |
423 |
list => [ |
424 |
qw(all meta_pkgs gui_pkgs all_updates security bugfix normal), |
425 |
if_($is_backports, 'backports') |
426 |
], |
427 |
format => sub { $views{$_[0]} }, text => $views{$default_list_mode}, |
428 |
tip => N("View"), |
429 |
changed => sub { |
430 |
my $val = $_[0]->get_text; |
431 |
return if $val eq $old_value; # workarounding gtk+ sending us sometimes twice events |
432 |
$old_value = $val; |
433 |
$default_list_mode = $rviews{$val}; |
434 |
if (my @cat = $wanted_categories{$rviews{$val}} && @{$wanted_categories{$rviews{$val}}}) { |
435 |
@$mandrakeupdate_wanted_categories = @cat; |
436 |
} |
437 |
|
438 |
if ($options->{tree_mode} ne $val) { |
439 |
$tree_mode->[0] = $options->{tree_mode} = $rviews{$val}; |
440 |
$tree_flat->[0] = $options->{state}{flat}; |
441 |
reset_search(); |
442 |
switch_pkg_list_mode($rviews{$val}); |
443 |
$options->{rebuild_tree}->(); |
444 |
} |
445 |
} |
446 |
); |
447 |
|
448 |
$options->{tree_submode} ||= $default_list_mode; |
449 |
$options->{tree_subflat} ||= $options->{state}{flat}; |
450 |
|
451 |
|
452 |
my $filter_box = gtknew( |
453 |
'ComboBox', |
454 |
list => [ qw(all installed non_installed) ], text => $filters{$filter->[0]}, |
455 |
format => sub { $filters{$_[0]} }, |
456 |
tip => N("Filter"), |
457 |
changed => sub { |
458 |
state $oldval; |
459 |
my $val = $_[0]->get_text; |
460 |
return if $val eq $oldval; # workarounding gtk+ sending us sometimes twice events |
461 |
$oldval = $val; |
462 |
$val = $rfilters{$val}; |
463 |
if ($filter->[0] ne $val) { |
464 |
$filter->[0] = $val; |
465 |
reset_search(); |
466 |
slow_func($::main_window->window, sub { switch_pkg_list_mode($default_list_mode) }); |
467 |
$options->{rebuild_tree}->(); |
468 |
} |
469 |
} |
470 |
); |
471 |
|
472 |
my $view_callback = sub { |
473 |
my ($val) = @_; |
474 |
return if $val eq $old_value; # workarounding gtk+ sending us sometimes twice events |
475 |
$old_value = $val; |
476 |
return if $mode->[0] eq $val; |
477 |
$mode->[0] = $val; |
478 |
$tree_flat->[0] = $options->{state}{flat} = member($mode->[0], qw(flat by_leaves by_selection by_size)); |
479 |
|
480 |
if ($options->{tree_mode} ne $val) { |
481 |
reset_search(); |
482 |
$options->{rebuild_tree}->(); |
483 |
} |
484 |
}; |
485 |
|
486 |
|
487 |
my @search_types = qw(normal descriptions summaries files); |
488 |
my $current_search_type = $search_types[0]; |
489 |
my $search_menu = Gtk2::Menu->new; |
490 |
my $i = 0; |
491 |
my $previous; |
492 |
foreach (N("in names"), N("in descriptions"), N("in summaries"), N("in file names")) { |
493 |
my ($name, $val) = ($_, $i); |
494 |
$search_menu->append(gtksignal_connect(gtkshow( |
495 |
$previous = Gtk2::RadioMenuItem->new_with_label($previous, $name)), |
496 |
activate => sub { $current_search_type = $search_types[$val] })); |
497 |
$i++; |
498 |
} |
499 |
|
500 |
my $info = Gtk2::Mdv::TextView->new; |
501 |
$info->set_left_margin(2); |
502 |
$info->set_right_margin(15); #- workaround when right elevator of scrolled window appears |
503 |
|
504 |
my $find_callback = sub { |
505 |
do_search($find_entry, $tree, $tree_model, $options, $current_search_type, $urpm, $pkgs); |
506 |
}; |
507 |
|
508 |
my $hpaned = gtknew('HPaned', position => $typical_width*0.9, |
509 |
child1 => gtknew('ScrolledWindow', child => $tree), |
510 |
resize1 => 0, shrink1 => 0, |
511 |
resize2 => 1, shrink2 => 0, |
512 |
child2 => gtknew('VPaned', |
513 |
child1 => gtknew('ScrolledWindow', child => $detail_list), resize1 => 1, shrink1 => 0, |
514 |
child2 => gtknew('ScrolledWindow', child => $info), resize2 => 1, shrink2 => 0 |
515 |
) |
516 |
); |
517 |
|
518 |
my $reload_db_and_clear_all = sub { |
519 |
slow_func($w->{real_window}->window, sub { |
520 |
$force_rebuild = 1; |
521 |
pkgs_provider({ skip_updating_mu => 1 }, $options->{tree_mode}); |
522 |
reset_search(); |
523 |
$size_selected = 0; |
524 |
$options->{rebuild_tree}->(); |
525 |
$find_callback->(); |
526 |
}); |
527 |
}; |
528 |
|
529 |
my $status = gtknew('Label'); |
530 |
my $checkbox_show_autoselect; |
531 |
my %check_boxes; |
532 |
my $auto_select_string = |
533 |
N("/_Options") . N("/_Select dependencies without asking"); |
534 |
my $clean_cache_string = |
535 |
N("/_Options") . "/" . |
536 |
N("Clear download cache after successful install"); |
537 |
my $updates_string = N("/_Options") . N("/_Compute updates on startup"); |
538 |
my $NVR_string = N("/_Options") . "/" . N("Search in _full package names"); |
539 |
my $regexp_search_string = N("/_Options") . "/" . N("Use _regular expressions in searches"); |
540 |
my ($menu, $factory) = create_factory_menu( |
541 |
$w->{real_window}, |
542 |
[ N("/_File"), undef, undef, undef, '<Branch>' ], |
543 |
if_( |
544 |
! $>, |
545 |
[ N("/_File") . N("/_Update media"), undef, sub { |
546 |
update_sources_interactive($urpm, transient => $w->{real_window}) |
547 |
and $reload_db_and_clear_all->(); |
548 |
}, undef, '<Item>' ] |
549 |
), |
550 |
[ N("/_File") . N("/_Reset the selection"), undef, sub { |
551 |
if ($MODE ne 'remove') { |
552 |
$urpm->disable_selected( |
553 |
open_rpm_db(), $urpm->{state}, |
554 |
map { if_($pkgs->{$_}{selected}, $pkgs->{$_}{pkg}) } keys %$pkgs, |
555 |
); |
556 |
} |
557 |
$pkgs->{$_}{selected} = 0 foreach keys %$pkgs; |
558 |
reset_search(); |
559 |
$size_selected = 0; |
560 |
$force_displaying_group = 1; |
561 |
my $tree_selection = $tree->get_selection; |
562 |
$tree_selection->select_path(Gtk2::TreePath->new_from_string('0')) if !$tree_selection->get_selected; |
563 |
$tree_selection->signal_emit('changed'); |
564 |
}, undef, '<Item>' ], |
565 |
[ N("/_File") . N("/Reload the _packages list"), undef, $reload_db_and_clear_all, undef, '<Item>' ], |
566 |
[ N("/_File") . N("/_Quit"), N("<control>Q"), \&quit, undef, '<Item>', ], |
567 |
#[ N("/_View"), undef, undef, undef, '<Branch>' ], |
568 |
if_(!$>, |
569 |
[ N("/_Options"), undef, undef, undef, '<Branch>' ], |
570 |
[ $auto_select_string, undef, |
571 |
sub { |
572 |
my $box = $check_boxes{$auto_select_string}; |
573 |
$auto_select->[0] = $box->get_active; |
574 |
$::rpmdrake_options{auto} = $box->get_active; |
575 |
$urpm->{options}{auto} = $box->get_active; |
576 |
}, |
577 |
undef, '<CheckItem>' ], |
578 |
[ $clean_cache_string, undef, |
579 |
sub { |
580 |
$clean_cache->[0] = |
581 |
$check_boxes{$clean_cache_string}->get_active; |
582 |
$::noclean = !$clean_cache->[0]; |
583 |
}, |
584 |
undef, '<CheckItem>' ], |
585 |
[ N("/_Options") . N("/_Media Manager"), undef, sub { |
586 |
require Rpmdrake::edit_urpm_sources; |
587 |
Rpmdrake::edit_urpm_sources::run() && $reload_db_and_clear_all->(); |
588 |
}, undef, '<Item>' ], |
589 |
[ N("/_Options") . N("/_Show automatically selected packages"), undef, sub { |
590 |
$dont_show_selections->[0] = !$checkbox_show_autoselect->get_active; |
591 |
}, undef, '<CheckItem>' ], |
592 |
|
593 |
[ $updates_string, undef, sub { |
594 |
$compute_updates->[0] = $check_boxes{$updates_string}->get_active; |
595 |
}, undef, '<CheckItem>' ], |
596 |
[ $NVR_string, undef, sub { |
597 |
$NVR_searches->[0] = $check_boxes{$NVR_string}->get_active; |
598 |
}, undef, '<CheckItem>' ], |
599 |
[ $regexp_search_string, undef, sub { |
600 |
$use_regexp->[0] = $check_boxes{$regexp_search_string}->get_active; |
601 |
}, undef, '<CheckItem>' ], |
602 |
), |
603 |
[ N("/_View"), undef, undef, undef, '<Branch>' ], |
604 |
(map { |
605 |
state ($idx, $previous); |
606 |
my $type = $idx ? join('/', N("/_View"), $previous) : '<RadioItem>'; |
607 |
$type =~ s/_//g; # gtk+ retrieve widgets by their path w/o any shortcut marks |
608 |
$previous = $modes{$_}; |
609 |
$idx++; |
610 |
my $val = $_; |
611 |
[ N("/_View") . '/' . $modes{$_}, undef, sub { $view_callback->($val) }, 0, $type ]; |
612 |
} qw(flat by_group by_leaves by_presence by_selection by_size by_source)), |
613 |
[ N("/_Help"), undef, undef, undef, '<Branch>' ], |
614 |
[ N("/_Help") . N("/_Report Bug"), undef, sub { run_drakbug('rpmdrake') }, undef, '<Item>' ], |
615 |
[ N("/_Help") . N("/_Help"), undef, sub { rpmdrake::open_help('') }, undef, '<Item>' ], |
616 |
[ N("/_Help") . N("/_About..."), undef, sub { |
617 |
my $license = formatAlaTeX(translate($::license)); |
618 |
$license =~ s/\n/\n\n/sg; # nicer formatting |
619 |
my $w = gtknew('AboutDialog', name => N("Rpmdrake"), |
620 |
version => $Rpmdrake::init::version, |
621 |
copyright => N("Copyright (C) %s by Mandriva", '2002-2009'), |
622 |
license => $license, wrap_license => 1, |
623 |
comments => N("Rpmdrake is Mageia package management tool."), |
624 |
website => 'http://www.mageia.org', |
625 |
website_label => N("Mageia"), |
626 |
authors => 'Thierry Vignaud <vignaud@mandriva.com>', |
627 |
artists => 'Hélène Durosini <ln@mandriva.com>', |
628 |
translator_credits => |
629 |
#-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") |
630 |
N("_: Translator(s) name(s) & email(s)\n"), |
631 |
transient_for => $::main_window, modal => 1, position_policy => 'center-on-parent', |
632 |
); |
633 |
$w->show_all; |
634 |
$w->run; |
635 |
}, undef, '<Item>' |
636 |
] |
637 |
|
638 |
); |
639 |
|
640 |
# to retrieve a path, one must prevent "accelerators completion": |
641 |
my $get_path = sub { return join('', map { my $i = $_; $i =~ s/_//g; $i } @_) }; |
642 |
|
643 |
if (my $widget = $factory->get_item('<main>' . $get_path->(N("/_View") . '/' . $modes{$mode->[0]}))) { |
644 |
$widget->set_active(1); |
645 |
} else { |
646 |
warn "Impossible to set $mode->[0] view as default\n"; |
647 |
} |
648 |
|
649 |
%check_boxes = map { |
650 |
$_ => $factory->get_widget("<main>" . $get_path->($_)); |
651 |
} ($auto_select_string, |
652 |
$clean_cache_string, |
653 |
$NVR_string, |
654 |
$updates_string, |
655 |
$regexp_search_string); |
656 |
|
657 |
if (!$>) { |
658 |
$check_boxes{$regexp_search_string}->set_active($use_regexp->[0]); |
659 |
$check_boxes{$NVR_string}->set_active($NVR_searches->[0]); |
660 |
$check_boxes{$auto_select_string}->set_active($auto_select->[0]); |
661 |
$check_boxes{$updates_string}->set_active($compute_updates->[0]); |
662 |
$check_boxes{$clean_cache_string}->set_active($clean_cache->[0]); |
663 |
} |
664 |
|
665 |
$checkbox_show_autoselect = $factory->get_widget("<main>" . strip_first_underscore(N("/_Options"), N("/_Show automatically selected packages"))) |
666 |
and $checkbox_show_autoselect->set_active(!$dont_show_selections->[0]); |
667 |
|
668 |
my $accel = Gtk2::AccelGroup->new; |
669 |
$accel->connect(Gtk2::Gdk->keyval_from_name('F'), ['control-mask'], ['visible'], sub { $find_entry->grab_focus() }); |
670 |
$w->{real_window}->add_accel_group($accel); |
671 |
|
672 |
gtkadd( |
673 |
$w->{window}, |
674 |
gtkpack_( |
675 |
gtknew('VBox', spacing => 3), |
676 |
0, $menu, |
677 |
if_(second(gtkroot()->get_size()) >= 600, 0, getbanner()), |
678 |
1, gtkadd( |
679 |
gtknew('Frame', border_width => 3, shadow_type => 'none'), |
680 |
gtkpack_( |
681 |
gtknew('VBox', spacing => 3), |
682 |
0, gtkpack_( |
683 |
gtknew('HBox', spacing => 10), |
684 |
0, $view_box, |
685 |
0, $filter_box, |
686 |
0, gtknew('Label', text => N("Find:")), |
687 |
1, $find_entry = gtknew('Entry', width => 260, |
688 |
primary_icon => 'gtk-find', |
689 |
secondary_icon => 'gtk-clear', |
690 |
tip => N("Please type in the string you want to search then press the <enter> key"), |
691 |
'icon-release' => $find_callback, |
692 |
'icon-press' => sub { |
693 |
my (undef, $pos, $event) = @_; |
694 |
# emulate Sexy::IconEntry's clear_button: |
695 |
if ($pos eq 'secondary') { |
696 |
$find_entry->set_text(''); |
697 |
reset_search(); |
698 |
} |
699 |
return if $pos ne 'primary'; |
700 |
$search_menu->popup(undef, undef, undef, undef, $event->button, $event->time); |
701 |
}, |
702 |
key_press_event => sub { |
703 |
member($_[1]->keyval, $Gtk2::Gdk::Keysyms{Return}, $Gtk2::Gdk::Keysyms{KP_Enter}) |
704 |
and $find_callback->(); |
705 |
}, |
706 |
), |
707 |
), |
708 |
1, $hpaned, |
709 |
0, $status, |
710 |
0, gtkpack_( |
711 |
gtknew('HBox', spacing => 20), |
712 |
0, gtksignal_connect( |
713 |
Gtk2::Button->new(but_(N("Select all"))), |
714 |
clicked => sub { |
715 |
toggle_all($options, 1); |
716 |
}, |
717 |
), |
718 |
1, gtknew('Label'), |
719 |
0, my $action_button = gtksignal_connect( |
720 |
Gtk2::Button->new(but_(N("Apply"))), |
721 |
clicked => sub { do_action($options, $callback_action, $info) }, |
722 |
), |
723 |
0, gtksignal_connect( |
724 |
Gtk2::Button->new(but_(N("Quit"))), |
725 |
clicked => \&quit, |
726 |
), |
727 |
), |
728 |
), |
729 |
), |
730 |
0, $statusbar = Gtk2::Statusbar->new, |
731 |
), |
732 |
); |
733 |
$action_button->set_sensitive(0) if $>; |
734 |
$find_entry->grab_focus; |
735 |
|
736 |
gtktext_insert($info, [ |
737 |
[ $info->render_icon('gtk-dialog-info', 'GTK_ICON_SIZE_DIALOG', undef) ], |
738 |
@{ ugtk2::markup_to_TextView_format( |
739 |
formatAlaTeX(join("\n\n\n", format_header(N("Quick Introduction")), |
740 |
N("You can browse the packages through the categories tree on the left."), |
741 |
N("You can view information about a package by clicking on it on the right list."), |
742 |
N("To install, update or remove a package, just click on its \"checkbox\".")))) |
743 |
} |
744 |
]); |
745 |
|
746 |
$w->{rwindow}->set_default_size($typical_width*2.7, 500) if !$::isEmbedded; |
747 |
$find_entry->set_text($rpmdrake_options{search}[0]) if $rpmdrake_options{search}; |
748 |
|
749 |
if ($rpmdrake_width->[0] && $rpmdrake_height->[0]) { |
750 |
# so that we can shrink back: |
751 |
$w->{real_window}->set_default_size($rpmdrake_width->[0], $rpmdrake_height->[0]); |
752 |
} |
753 |
$w->{rwindow}->show_all; |
754 |
$w->{rwindow}->set_sensitive(0); |
755 |
|
756 |
# ensure treeview get realized so that ->get_selection returns something |
757 |
$detail_list->realize; |
758 |
gtkflush(); |
759 |
|
760 |
slow_func($::main_window->window, sub { pkgs_provider({}, $default_list_mode) }); # default mode |
761 |
if (@initial_selection) { |
762 |
$options->{initial_selection} = \@initial_selection; |
763 |
$pkgs->{$_}{selected} = 0 foreach @initial_selection; |
764 |
} |
765 |
|
766 |
$w->{rwindow}->set_sensitive(1); |
767 |
|
768 |
$options->{widgets} = { |
769 |
w => $w, |
770 |
tree => $tree, |
771 |
tree_model => $tree_model, |
772 |
detail_list_model => $detail_list_model, |
773 |
detail_list => $detail_list, |
774 |
info => $info, |
775 |
status => $status, |
776 |
}; |
777 |
$options->{init_callback} = $find_callback if $rpmdrake_options{search}; |
778 |
|
779 |
ask_browse_tree_given_widgets_for_rpmdrake($options); |
780 |
} |
781 |
|
782 |
|
783 |
# -=-=-=---=-=-=---=-=-=-- main -=-=-=---=-=-=---=-=-=- |
784 |
|
785 |
|
786 |
if (my $pid = is_running('rpmdrake')) { |
787 |
interactive_msg(N("Warning"), N("rpmdrake is already running (pid: %s)", $pid), yesno => [ N("Quit") ]); |
788 |
exit(0); |
789 |
} |
790 |
|
791 |
$w = ugtk2->new(N("Software Management")); |
792 |
$w->{rwindow}->show_all if $::isEmbedded; |
793 |
|
794 |
readconf(); |
795 |
|
796 |
warn_about_user_mode(); |
797 |
|
798 |
do_merge_if_needed(); |
799 |
|
800 |
|
801 |
init(); |
802 |
|
803 |
run_treeview_dialog(\&perform_installation); |
804 |
|
805 |
writeconf(); |
806 |
|
807 |
myexit(0); |