summaryrefslogtreecommitdiffstats
path: root/perl-install/pixmaps/default.png
blob: 97e63ff9dab0ed2c284bf704ee8ddadeb590dca7 (plain)
ofshex dumpascii
0000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 30 00 00 00 30 08 06 00 00 00 57 02 f9 .PNG........IHDR...0...0.....W..
0020 87 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00 00 06 62 4b 47 44 00 ff 00 ff 00 ff a0 bd a7 93 .....sRGB.........bKGD..........
0040 00 00 00 09 70 48 59 73 00 00 0b 12 00 00 0b 12 01 d2 dd 7e fc 00 00 00 07 74 49 4d 45 07 db 01 ....pHYs...........~.....tIME...
0060 18 14 25 10 f2 8b db a3 00 00 04 2c 49 44 41 54 68 de d5 99 5d 68 1c 55 18 86 9f 33 fb 97 9f 4d ..%........,IDATh...]h.U...3...M
0080 0c 69 4c 4c 4c 75 63 28 34 b6 74 8d 2d b5 e2 3a b6 77 8a 38 82 57 5e f8 73 61 71 c0 3b d1 60 44 .iLLLuc(4.t.-..:.w.8.W^.saq.;.`D
00a0 bc 10 44 ad a1 62 b1 8a 23 28 88 05 51 14 64 7a 91 de a8 e9 5c 34 14 69 fd 41 1a 95 4a a2 69 93 ..D..b..#(..Q.dz....\4.i.A..J.i.
00c0 a0 c6 c6 a4 4d 9a 64 77 8f 17 3b d5 65 b3 d9 cc 5f 96 d9 17 f6 66 67 e7 9c f7 3d df 77 be ef 3d ....M.dw..;.e..._....fg...=.w..=
00e0 67 85 94 92 f5 a0 eb 3a 95 60 99 46 06 18 92 32 b7 4f 88 08 1e b0 0c 0c 01 87 55 4d 9f 2b 7d 68 g......:.`.F...2.O........UM.+}h
0100 18 c6 86 03 44 dd ce 68 99 46 0a d0 b3 b9 a9 c1 a8 cd d9 23 79 80 04 f0 02 70 8f 65 1a 4f ab 9a ....D..h.F.........#y....p.e.O..
0120 7e c6 ed 00 8a 0b e2 19 cb 34 46 a5 cc 8f 03 83 d1 48 17 01 41 00 77 03 27 2d d3 78 22 70 01 96 ~........4F......H..A.w.'-.x"p..
0140 69 74 58 a6 f1 16 30 02 ec 13 42 61 93 d0 08 1c b1 4c 63 77 60 02 2c d3 48 02 03 c0 53 40 84 cd itX...0...Ba.....Lcw`.,.H...S@..
0160 47 23 f0 46 90 11 e8 04 1e 75 93 6a 01 20 13 88 80 be 4e 52 c0 33 40 3b d5 85 62 99 c6 7e c7 1b G#.F.....u.j......NR.3@;..b..~..
0180 68 bd 32 da d7 c9 e3 c0 eb de 04 e4 90 80 f0 9e 75 13 c0 81 b1 69 26 fc 94 d1 94 1b f2 92 3c 7d h.2.............u....i&.......<}
01a0 b7 7f 40 7b f7 37 d7 2a 0b 96 69 a4 9a 3b de 1d 9f 99 dc 41 7d 2c 83 10 c2 e9 70 29 40 07 9e f7 ..@{.7.*..i..;.....A},....p)@...
01c0 b3 07 fe 00 a4 d3 19 6f d8 3a ca 4f 67 0f 36 5f 23 0f a0 6a fa c4 6d 77 7c 2b 3a b7 0d 08 c8 b9 .......o.:.Og.6_#..j..mw|+:.....
01e0 8d c2 23 7e 37 f1 25 29 a5 e3 25 bb f1 96 af 50 35 7d a1 dc b3 74 3a ed a5 0e 74 fb 15 d0 e1 22 ..#~7.%)..%....P5}...t:...t...."
0200 e4 9c 3d f9 62 c5 fa ed a1 7f cc f9 2e a3 52 e6 d6 64 7a 59 3f 50 f7 37 c0 42 85 7e 12 77 4a a8 ..=.b.........R..dzY?P.7.B.~.wJ.
0220 38 03 fc 0a f8 7e 69 75 14 29 b3 85 15 54 16 10 d1 49 20 5f 32 c2 25 96 af b6 1e 04 a6 2b 8c d5 8....~iu.)...T...I._2.%......+..
0240 03 d4 bb 14 70 c5 c9 8f 2a 55 a1 ef 1a e2 99 03 40 ba 77 e7 27 6f 36 34 4d 11 4f cc 73 75 69 0b ....p...*U......@.w.'o64M.O.sui.
0260 cb 8b ad 08 91 a7 3e 39 c3 0f a7 9e 6d 53 35 7d 76 03 f3 f7 a1 6d dc dc 60 ca 57 1f 70 62 a7 1d ......>9....mS5}v....m..`.W.pb..
0280 9a c0 a3 b6 15 11 2e 5e 93 c0 c0 d8 34 87 03 73 a3 3e 70 bf 4b f2 00 7f 02 9f fb 4d 21 bf 2b 9f .......^....4..s.>p.K......M!.+.
02a0 04 1e 96 32 9b 12 c2 f5 34 ef a8 9a 3e 01 46 f5 05 58 a6 d1 02 dc 0b 0c 4a 29 d3 1e c8 67 ed 3d ...2....4...>.F..X......J)...g.=
02c0 43 d5 23 60 99 c6 7d c0 fb b6 8b 75 63 1d 8a 52 5f 1c 2b ac 7e 95 05 58 a6 f1 24 70 14 88 79 3a C.#`..}....uc..R_.+.~..X..$p..y:
02e0 96 89 1c 52 46 5e 01 de 76 f3 5e 34 20 f2 b7 02 47 bc 91 cf d3 d8 7c 91 2b f3 5b 35 55 d3 8f bb ...RF^..v.^4....G.....|.+.[5U...
0300 7d 3b a8 08 bc 04 d4 b9 4b 16 49 53 cb 6f 5c 9e 4b ed d9 bd ff e5 33 5e 27 8e 06 b0 fa 0f 00 0f };......K.IS.o\.K.....3^'.......
0320 b9 b1 dd d1 c4 39 76 ee 19 e6 ba 2d e7 85 df f9 a3 3e c9 27 81 43 e5 fb 89 5c 53 fe 25 92 96 eb .....9v....-.....>.'.C...\S.%...
0340 47 e8 eb 1f 26 5e 37 2f 82 08 bd df 46 d6 0b 6c 5b ff b6 a4 64 32 65 86 ed fd 27 02 23 1f 84 80 G...&^7/....F..l[...d2e...'.#...
0360 9e 72 51 54 94 7c f9 1b 82 9b c7 48 d4 fd 13 18 f9 4d b3 12 f9 7c f9 61 57 57 92 c1 df 00 f8 7c .rQT.|.....H.....M...|.aWW.....|
0380 7f dc 95 c1 b9 b8 f7 cb b0 09 f8 75 ed 01 a1 22 c6 42 25 40 d5 f4 cb c0 d7 4e 0e ff 89 fa bf 00 ...........u...".B%@.....N......
03a0 8e 85 2d 02 00 cf 01 5f d8 47 c6 15 fe bf 7e c8 02 17 6c 6f f4 e0 f2 52 5b 97 aa e9 a7 83 16 10 ..-...._.G....~...lo...R[.......
03c0 44 27 fe 19 38 01 74 d9 26 6e d2 76 93 1f db 11 da 54 04 21 60 3b f0 aa 94 b2 d5 76 9f 37 81 bc D'..8.t.&n.v.....T.!`;.....v.7..
03e0 0b b2 ef 59 c7 87 90 b2 09 81 40 b2 88 a0 61 50 d5 f4 43 61 4b a1 5d 45 e4 8b 9a 58 0c 6c f2 85 ...Y......@...aP..CaK.]E...X.l..
0400 6f 1a 80 dc 6b b6 e5 0e 95 80 0a c5 bd b4 67 45 a0 70 df 1a 2a 01 53 eb 57 d2 b2 c5 69 47 d8 04 o...k.........gE.p..*.S.W...iG..
0420 fc b8 92 fb 05 89 b4 09 17 7f 04 b5 b0 89 17 23 4a eb 7f b9 ee 00 e7 c2 16 81 e6 a8 d2 e1 f0 d8 ...............#J...............
0440 b8 04 85 bf ac 42 15 01 c7 47 99 64 db a7 f4 df 79 6a 38 6c 11 98 5d cd 5d d8 d8 a1 8a 69 5a da .....B...G.d....yj8l..].]....iZ.
0460 47 42 e7 46 01 66 63 91 ee c7 94 d8 79 db 3d 94 56 20 49 34 fe 3b bb f6 7e 46 4f 6f 5c 84 6e 13 GB.F.fc.....y.=.V.I4.;..~FOo\.n.
0480 ab 9a be 02 7c 64 7f aa 0e 85 1a 47 c5 db e9 5a 40 cd 47 a0 e6 05 fc 0b df 68 3f b0 f3 f7 a0 31 ....|d.....G...Z@.G......h?....1
04a0 00 00 00 00 49 45 4e 44 ae 42 60 82 ....IEND.B`.
d='n253' href='#n253'>253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
package interactive::gtk; # $Id$

use diagnostics;
use strict;
use vars qw(@ISA);

@ISA = qw(interactive);

use interactive;
use common;
use ugtk2 qw(:helpers :wrappers :create);
use Gtk2::Gdk::Keysyms;

my $forgetTime = 1000; #- in milli-seconds

sub new {
    ($::windowwidth, $::windowheight) = gtkroot()->get_size if !$::isInstall;
    goto &interactive::new;
}
sub enter_console { my ($o) = @_; $o->{suspended} = common::setVirtual(1) }
sub leave_console { my ($o) = @_; common::setVirtual(delete $o->{suspended}) }

sub exit { ugtk2::exit(@_) }

sub ask_fileW {
    my ($_o, $title, $dir) = @_;
    my $w = ugtk2->new($title);
    $dir .= '/' if $dir !~ m|/$|;
    ugtk2::_ask_file($w, $title, $dir); 
    $w->main;
}

sub create_boxradio {
    my ($e, $may_go_to_next, $changed, $double_click) = @_;

    my $boxradio = gtkshow(gtkpack2__(Gtk2::VBox->new(0, 0),
                                      my @radios = gtkradio('', @{$e->{formatted_list}})));
    my $tips = Gtk2::Tooltips->new;
    mapn {
	my ($txt, $w) = @_;
	$w->signal_connect(button_press_event => $double_click) if $double_click;

	$w->signal_connect(key_press_event => sub {
	    &$may_go_to_next;
	});
	$w->signal_connect(clicked => sub {
 	    ${$e->{val}} = $txt;
	    &$changed;
        });
	if ($e->{help}) {
	    gtkset_tip($tips, $w,
		       ref($e->{help}) eq 'HASH' ? $e->{help}{$txt} :
		       ref($e->{help}) eq 'CODE' ? $e->{help}($txt) : $e->{help});
	}
    } $e->{list}, \@radios;

    $boxradio, sub {
	my ($v, $full_struct) = @_;
	mapn { 
	    $_[0]->set_active($_[1] eq $v);
	    $full_struct->{focus_w} = $_[0] if $_[1] eq $v;
	} \@radios, $e->{list};
    }, $radios[0];
}

sub create_treeview_list {
    my ($e, $may_go_to_next, $changed, $double_click) = @_;
    my $curr;

    my $list = Gtk2::ListStore->new("Glib::String");
    my $list_tv = Gtk2::TreeView->new_with_model($list);
    $list_tv->set_headers_visible(0);
    $list_tv->get_selection->set_mode('browse');
    my $textcolumn = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0);
    $list_tv->append_column($textcolumn);
    
    my $select = sub {
	$list_tv->set_cursor($_[0], undef, 0);
    	$list_tv->scroll_to_cell($_[0], undef, 1, 0.5, 0);
    };

    my ($starting_word, $start_reg) = ('', '^');
    my $timeout;
    $list_tv->signal_connect(key_press_event => sub {
        my ($_w, $event) = @_;
	my $c = chr($event->keyval & 0xff);

	Glib::Source->remove($timeout) if $timeout; $timeout = '';
	
	if ($event->keyval >= 0x100) {
	    &$may_go_to_next if member($event->keyval, ($Gtk2::Gdk::Keysyms{Return}, $Gtk2::Gdk::Keysyms{KP_Enter}));
	    $starting_word = '' if !member($event->keyval, ($Gtk2::Gdk::Keysyms{Control_L}, $Gtk2::Gdk::Keysyms{Control_R}));
	} else {
	    if (member('control-mask', @{$event->state})) {
		$c eq 's' or return 1;
		$start_reg and $start_reg = '', return 1;
		$curr++;
	    } else {
		&$may_go_to_next if $c eq ' ';

		$curr++ if $starting_word eq '' || $starting_word eq $c;
		$starting_word .= $c unless $starting_word eq $c;
	    }
	    my @l = @{$e->{formatted_list}};
	    my $word = quotemeta $starting_word;
	    my $j; for ($j = 0; $j < @l; $j++) {
		 $l[($j + $curr) % @l] =~ /$start_reg$word/i and last;
	    }
	    if ($j == @l) {
		$starting_word = '';
	    } else {
		$select->(Gtk2::TreePath->new_from_string(($j + $curr) % @l));
	    }

	    $timeout = Glib::Timeout->add($forgetTime, sub { $timeout = $starting_word = ''; 0 });
	}
	0;
    });
    $list_tv->show;

    $list->append_set([ 0 => $_ ]) foreach @{$e->{formatted_list}};

    $list_tv->get_selection->signal_connect(changed => sub {
	my ($model, $iter) = $_[0]->get_selected;
	$model && $iter or return;
	my $row = $model->get_path_str($iter);
	${$e->{val}} = $e->{list}[$curr = $row];
	&$changed;
    });
    $list_tv->signal_connect(button_press_event => $double_click) if $double_click;

    $list_tv, sub {
	my ($v) = @_;
	eval {
	    my $nb = find_index { $_ eq $v } @{$e->{list}};
	    my ($old_path) = $list_tv->get_cursor;
	    if (!$old_path || $nb != $old_path->to_string) {
		$select->(Gtk2::TreePath->new_from_string($nb));
	    }
	    undef $old_path if $old_path;
	};
    };
}

sub create_treeview_tree {
    my ($e, $may_go_to_next, $changed, $double_click, $tree_expanded) = @_;

    $tree_expanded = to_bool($tree_expanded); #- to reduce "Use of uninitialized value", especially when debugging

    my $sep = quotemeta $e->{separator};
    my $tree_model = Gtk2::TreeStore->new("Glib::String", "Gtk2::Gdk::Pixbuf", "Glib::String");
    my $tree = Gtk2::TreeView->new_with_model($tree_model);
    $tree->get_selection->set_mode('browse');
    $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0));
    $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererPixbuf->new, 'pixbuf' => 1));
    $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 2));
    $tree->set_headers_visible(0);

    my ($build_value, $clean_image);
    if (exists $e->{image2f}) {
	my $to_unref;
	$build_value = sub {
	    my ($text, $image) = $e->{image2f}->($_[0]);
	    [ $text  ? (0 => $text) : @{[]},
	      $image ? (1 => $to_unref = gtkcreate_pixbuf($image)) : @{[]} ];
	};
	$clean_image = sub { undef $to_unref };
    } else {
	$build_value = sub { [ 0 => $_[0] ] };
	$clean_image = sub {};
    }

    my (%wtree, %wleaves, $size, $selected_via_click);
    my $parent; $parent = sub {
	if (my $w = $wtree{"$_[0]$e->{separator}"}) { return $w }
	my $s = '';
	foreach (split $sep, $_[0]) {
	    $wtree{"$s$_$e->{separator}"} ||= 
	      $tree_model->append_set($s ? $parent->($s) : undef, $build_value->($_));
	    $clean_image->();
	    $size++ if !$s;
	    $s .= "$_$e->{separator}";
	}
	$wtree{$s};
    };

    #- do some precomputing to not slowdown selection change and key press
    my (%precomp, @ordered_keys);
    mapn {
	my ($root, $leaf) = $_[0] =~ /(.*)$sep(.+)/ ? ($1, $2) : ('', $_[0]);
	my $iter = $tree_model->append_set($parent->($root), $build_value->($leaf));
	$clean_image->();
	my $pathstr = $tree_model->get_path_str($iter);
	$precomp{$pathstr} = { value => $leaf, fullvalue => $_[0], listvalue => $_[1] };
	push @ordered_keys, $pathstr;
	$wleaves{$_[0]} = $pathstr;
    } $e->{formatted_list}, $e->{list};
    undef $_ foreach values %wtree;
    undef %wtree;

    my $select = sub {
	my ($path_str) = @_;
	$tree->expand_to_path(Gtk2::TreePath->new_from_string($path_str));
	my $path = Gtk2::TreePath->new_from_string($path_str);
	$tree->set_cursor($path, undef, 0);
        gtkflush();  #- workaround gtk2 bug not honouring centering on the given row if node was closed
	$tree->scroll_to_cell($path, undef, 1, 0.5, 0);
    };

    my $curr = $tree_model->get_iter_first; #- default value
    $tree->expand_all if $tree_expanded;

    $tree->get_selection->signal_connect(changed => sub {
	my ($model, $iter) = $_[0]->get_selected;
	$model && $iter or return;
	undef $curr if ref $curr;
	my $path = $tree_model->get_path($curr = $iter);
	if (!$tree_model->iter_has_child($iter)) {
	    ${$e->{val}} = $precomp{$path->to_string}{listvalue};
	    &$changed;
	} else {
	    $tree->expand_row($path, 0) if $selected_via_click;
	}
    });
    my ($starting_word, $start_reg) = ('', "^");
    my $timeout;

    my $toggle = sub {
	if ($tree_model->iter_has_child($curr)) {
	    $tree->toggle_expansion($tree_model->get_path($curr), 0);

	} else {
	    &$may_go_to_next;
	}
    };

    $tree->signal_connect(key_press_event => sub {
        my ($_w, $event) = @_;
	$selected_via_click = 0;
	my $c = chr($event->keyval & 0xff);
	$curr or return;
	Glib::Source->remove($timeout) if $timeout; $timeout = '';

	if ($event->keyval >= 0x100) {
	    &$toggle if member($event->keyval, ($Gtk2::Gdk::Keysyms{Return}, $Gtk2::Gdk::Keysyms{KP_Enter}));
	    $starting_word = '' if !member($event->keyval, ($Gtk2::Gdk::Keysyms{Control_L}, $Gtk2::Gdk::Keysyms{Control_R}));
	} else {
	    my $next;
	    if (member('control-mask', @{$event->state})) {
		$c eq "s" or return 1;
		$start_reg and $start_reg = '', return 1;
		$next = 1;
	    } else {
		&$toggle if $c eq ' ';
		$next = 1 if $starting_word eq '' || $starting_word eq $c;
		$starting_word .= $c unless $starting_word eq $c;
	    }
	    my $word = quotemeta $starting_word;
	    my ($after, $best);

	    my $currpath = $tree_model->get_path_str($curr);
	    foreach my $v (@ordered_keys) { 
		$next &&= !$after;
		$after ||= $v eq $currpath;
		if ($precomp{$v}{value} =~ /$start_reg$word/i) {
		    if ($after && !$next) {
			($best, $after) = ($v, 0);
		    } else {
			$best ||= $v;
		    }
		}
	    }

	    if (defined $best) {
		$select->($best);
	    } else {
		$starting_word = '';
	    }

	    $timeout = Glib::Timeout->add($forgetTime, sub { $timeout = $starting_word = ''; 0 });
	}
	0;
    });
    $tree->signal_connect(button_press_event => sub {
	$selected_via_click = 1;
	&$double_click if $curr && !$tree_model->iter_has_child($curr) && $double_click;
    });

    $tree, sub {
	my $v = may_apply($e->{format}, $_[0]);
	my ($model, $iter) = $tree->get_selection->get_selected;
	$select->($wleaves{$v} || return) if !$model || $wleaves{$v} ne $model->get_path_str($iter);
	undef $iter if ref $iter;
    };
}

sub create_list {
    my ($e, $may_go_to_next, $changed, $double_click) = @_;
    my $l = $e->{list};
    my $list = Gtk2::List->new;
    $list->set_selection_mode('browse');

    my $select = sub {
	$list->select_item($_[0]);
    };

    my $tips = Gtk2::Tooltips->new;
    each_index {
	my $item = Gtk2::ListItem->new(may_apply($e->{format}, $_));
	$item->signal_connect(key_press_event => sub {
    	    my ($_w, $event) = @_;
    	    my $c = chr($event->keyval & 0xff);
	    &$may_go_to_next if $event->keyval < 0x100 ? $c eq ' ' : $c eq "\r" || $c eq "\x8d";
    	    0;
    	});
	$list->append_items(gtkshow($item));
	if ($e->{help}) {
	    gtkset_tip($tips, $item,
		       ref($e->{help}) eq 'HASH' ? $e->{help}{$_} :
		       ref($e->{help}) eq 'CODE' ? $e->{help}($_) : $e->{help});
	}
	$item->grab_focus if ${$e->{val}} && $_ eq ${$e->{val}};
    } @$l;

    #- signal_connect'ed after append_items otherwise it is called and destroys the default value
    $list->signal_connect(select_child => sub {
	my ($_w, $row) = @_;
	${$e->{val}} = $l->[$list->child_position($row)];
	&$changed;
    });
    $list->signal_connect(button_press_event => $double_click) if $double_click;

    $list, sub { 
	my ($v) = @_;
	eval { 
	    $select->(find_index { $_ eq $v } @$l);
	};
    };
}

#- $actions is a ref list of $action
#- $action is a { kind => $kind, action => sub { ... }, button => Gtk2::Button->new(...) }
#-   where $kind is one of '', 'modify', 'remove', 'add'
sub add_modify_remove_action {
    my ($button, $buttons, $e, $treelist) = @_;

    if (member($button->{kind}, 'modify', 'remove')) {
	@{$e->{list}} or return;
    }
    my $r = $button->{action}->(${$e->{val}});
    defined $r or return;
    
    if ($button->{kind} eq 'add') {
	${$e->{val}} = $r;
    } elsif ($button->{kind} eq 'remove') {
	${$e->{val}} = $e->{list}[0];
    }
    ugtk2::gtk_set_treelist($treelist, [ map { may_apply($e->{format}, $_) } @{$e->{list}} ]);

    add_modify_remove_sensitive($buttons, $e);
    1;
}

sub add_modify_remove_sensitive {
    my ($buttons, $e) = @_;
    $_->{button}->set_sensitive(@{$e->{list}} != ()) foreach 
      grep { member($_->{kind}, 'modify', 'remove') } @$buttons;
}

sub ask_fromW {
    my ($o, $common, $l, $l2) = @_;
    my $ignore = 0; #-to handle recursivity

    my $mainw = ugtk2->new($common->{title}, %$o, modal => 1, if__($::main_window, transient => $::main_window));
 
    #-the widgets
    my (@widgets, @widgets_always, @widgets_advanced, $advanced);
    my $tooltips = Gtk2::Tooltips->new;
    my $ok_clicked = sub { 
	!$mainw->{ok} || $mainw->{ok}->get_property('sensitive') or return;
	$mainw->{retval} = 1;
	Gtk2->main_quit;
    };
    my $set_all = sub {
	$ignore = 1;
	$_->{set}->(${$_->{e}{val}}, $_) foreach @widgets_always, @widgets_advanced;
	$_->{real_w}->set_sensitive(!$_->{e}{disabled}()) foreach @widgets_always, @widgets_advanced;
	$mainw->{ok}->set_sensitive(!$common->{callbacks}{ok_disabled}()) if $common->{callbacks}{ok_disabled};
	$ignore = 0;
    };
    my $get_all = sub {
	${$_->{e}{val}} = $_->{get}->() foreach @widgets_always, @widgets_advanced;
    };
    my $update = sub {
	my ($f) = @_;
	return if $ignore;
	$get_all->();
	$f->();
	$set_all->();
    };

    my $label_sizegrp = Gtk2::SizeGroup->new('horizontal');
    my $realw_sizegrp = Gtk2::SizeGroup->new('horizontal');

    my $create_widget = sub {
	my ($e, $ind) = @_;

	my $may_go_to_next = sub {
	    my (undef, $event) = @_;
	    if (!$event || ($event->keyval & 0x7f) == 0xd) {
		if ($ind == $#widgets) {
		    @widgets == 1 ? $ok_clicked->() : $mainw->{ok}->grab_focus;
		} else {
		    $widgets[$ind+1]{focus_w}->grab_focus;
		}
		return 1;  #- prevent an action on the just grabbed focus
	    }
	};
	my $changed = sub { $update->(sub { $common->{callbacks}{changed}($ind) }) };

	my ($w, $real_w, $focus_w, $set, $get, $grow);
	if ($e->{type} eq 'iconlist') {
	    $w = Gtk2::Button->new;
	    $set = sub {
		gtkdestroy($e->{icon});
		my $f = $e->{icon2f}->($_[0]);
		$e->{icon} = -e $f ?
		    gtkcreate_img($f) :
		    Gtk2::WrappedLabel->new(may_apply($e->{format}, $_[0]));
		$w->add(gtkshow($e->{icon}));
	    };
	    $w->signal_connect(clicked => sub {
		$set->(${$e->{val}} = next_val_in_array(${$e->{val}}, $e->{list}));
		$changed->();
	    });
	    $real_w = gtkpack_(Gtk2::HBox->new(0,10), 1, Gtk2::HBox->new(0,0), 0, $w, 1, Gtk2::HBox->new(0,0));
	} elsif ($e->{type} eq 'bool') {
	    if ($e->{image}) {
		$w = gtkadd(Gtk2::CheckButton->new, gtkshow(gtkcreate_img($e->{image})));
	    } else {
#-		warn "\"text\" member should have been used instead of \"label\" one at:\n", common::backtrace(), "\n" if $e->{label} && !$e->{text};
		$w = Gtk2::CheckButton->new_with_label($e->{text});
	    }
	    $w->signal_connect(clicked => $changed);
	    $set = sub { $w->set_active($_[0]) };
	    $get = sub { $w->get_active };
	} elsif ($e->{type} eq 'label') {
	    $w = Gtk2::WrappedLabel->new(${$e->{val}});
	    $set = sub { $w->set($_[0]) };
	} elsif ($e->{type} eq 'button') {
	    $w = Gtk2::Button->new_with_label('');
	    $w->signal_connect(clicked => sub {
		$get_all->();
		$mainw->{rwindow}->hide;
		if (my $v = $e->{clicked_may_quit}()) {
		    $mainw->{retval} = $v;
		    Gtk2->main_quit;
		}
		$mainw->{rwindow}->show;
		$set_all->();
	    });
	    $set = sub { $w->child->set_label(may_apply($e->{format}, $_[0])) };
	} elsif ($e->{type} eq 'range') {
	    my $want_scale = !$::expert;
	    my $adj = Gtk2::Adjustment->new(${$e->{val}}, $e->{min}, $e->{max} + ($want_scale ? 1 : 0), 1, ($e->{max} - $e->{min}) / 10, 1);
	    $adj->signal_connect(value_changed => $changed);
	    $w = $want_scale ? Gtk2::HScale->new($adj) : Gtk2::SpinButton->new($adj, 10, 0);
	    $w->set_size_request($want_scale ? 200 : 100, -1);
	    $w->set_digits(0);
	    $w->signal_connect(key_press_event => $may_go_to_next);
	    $set = sub { $adj->set_value($_[0]) };
	    $get = sub { $adj->get_value };
	} elsif ($e->{type} =~ /list/) {

	    $e->{formatted_list} = [ map { may_apply($e->{format}, $_) } @{$e->{list}} ];

	    if (my $actions = $e->{add_modify_remove}) {
		my @buttons = map {
		    { kind => lc $_, action => $actions->{$_}, button => Gtk2::Button->new(translate($_)) };
		} N_("Add"), N_("Modify"), N_("Remove");
		my $modify = find { $_->{kind} eq 'modify' } @buttons;

		my $do_action = sub {
		    my ($button) = @_;
		    add_modify_remove_action($button, \@buttons, $e, $w) and $changed->();
		};

		($w, $set, $focus_w) = create_treeview_list($e, $may_go_to_next, $changed, 
							    sub { $do_action->($modify) if $_[1]->type =~ /^2/ });
		$e->{saved_default_val} = ${$e->{val}};

		foreach my $button (@buttons) {
		    $button->{button}->signal_connect(clicked => sub { $do_action->($button) });
		}
		add_modify_remove_sensitive(\@buttons, $e);

		$real_w = gtkpack_(Gtk2::HBox->new(0,0),
				   1, create_scrolled_window($w), 
				   0, gtkpack__(Gtk2::VBox->new(0,0), map { $_->{button} } @buttons));
		$grow = 1;
	    } else {

		my $quit_if_double_click = 
		  #- i'm the only one, double click means accepting
		  @$l == 1 || $e->{quit_if_double_click} ? 
		    sub { $_[1]->type =~ /^2/ && $ok_clicked->() } : ''; 

		my @para = ($e, $may_go_to_next, $changed, $quit_if_double_click);
		my $use_boxradio = exists $e->{gtk}{use_boxradio} ? $e->{gtk}{use_boxradio} : @{$e->{list}} <= 8;

		if ($e->{help}) {
		    #- used only when needed, as key bindings are dropped by List (ListStore does not seems to accepts Tooltips).
		    ($w, $set, $focus_w) = $use_boxradio ? create_boxradio(@para) : create_list(@para);
		} elsif ($e->{type} eq 'treelist') {
		    ($w, $set) = create_treeview_tree(@para, $e->{tree_expanded});
		    $e->{saved_default_val} = ${$e->{val}}; #- during realization, signals will mess up the default val :(
		} else {
		    if ($use_boxradio) {
			($w, $set, $focus_w) = create_boxradio(@para);
		    } else {
			($w, $set, $focus_w) = create_treeview_list(@para);
			$e->{saved_default_val} = ${$e->{val}};
		    }
		}
		if (@{$e->{list}} > 10) {
		    $real_w = create_scrolled_window($w);
		    $grow = 1;
		}
	    }
	} else {
	    if ($e->{type} eq "combo") {

		my @formatted_list = map { may_apply($e->{format}, $_) } @{$e->{list}};

		my @l = sort { $b <=> $a } map { length } @formatted_list;
		my $width = $l[@l / 16]; # take the third octile (think quartile)

		if ($e->{not_edit} && $width < 60) { #- OptionMenus do not have an horizontal scroll-bar. This can cause havoc for long strings (eg: diskdrake Create dialog box in expert mode)
		    $w = Gtk2::OptionMenu->new;
		} else {
		    $w = Gtk2::Combo->new;
		    $w->set_use_arrows_always(1);
		    $w->entry->set_editable(!$e->{not_edit});
		    $w->disable_activate;
		}

		$w->set_popdown_strings(@formatted_list);
		($real_w, $w) = ($w, $w->entry);

		#- FIXME workaround gtk suckiness (set_text generates two 'change' signals, one when removing the whole, one for inserting the replacement..)
		my $idle;
		$w->signal_connect(changed => sub {
		    $idle ||= Glib::Idle->add(sub { undef $idle; $changed->(); 0 });
		});

		$set = sub {
		    my $s = may_apply($e->{format}, $_[0]);
		    $w->set_text($s) if $s ne $w->get_text && $_[0] ne $w->get_text;
		};
		$get = sub { 
		    my $s = $w->get_text;
		    my $i = eval { find_index { $s eq $_ } @formatted_list };
		    defined $i ? $e->{list}[$i] : $s;
		};
	    } else {
                $w = Gtk2::Entry->new;
		$w->signal_connect(changed => $changed);
		$w->signal_connect(focus_in_event => sub { $w->select_region(0, -1) });