#!/usr/bin/perl use strict; use lib qw(/usr/lib/libDrakX); use standalone; use common; use ugtk2 qw(:create :dialogs :helpers :wrappers); use interactive; use bootsplash; my $in = 'interactive'->vnew('su'); $in->do_pkgs->ensure_is_installed('ImageMagick', '/usr/bin/convert') or close_all(); $in->do_pkgs->ensure_is_installed('netpbm', '/usr/bin/jpegtopnm') or close_all(); my $window = ugtk2->new('DrakSplash'); $window->{rwindow}->signal_connect(delete_event => \&close_all); my @image_pixbuf; my $current_rect = []; my $current_point; my $preview_window = ugtk2->new('DrakSplash'); my $image_area = create_image_area(); my $scroll = create_scrolled_window($image_area); switch_to_mode(0); my %scale_settings = ( tx => [ 1/10, N("x coordinate of text box\nin number of characters") ], ty => [ 1/10, N("y coordinate of text box\nin number of characters") ], tw => [ 8/10, N("text width") ], th => [ 8/10, N("text box height") ], px => [ 2/10, N("the progress bar x coordinate\nof its upper left corner") ], py => [ 7/10, N("the progress bar y coordinate\nof its upper left corner") ], pw => [ 6/10, N("the width of the progress bar") ], ph => [ 1/10, N("the height of the progress bar") ], ); my %adj; create_adj_widgets(); my %theme; set_theme('new_theme'); set_resolution([ bootsplash::get_framebuffer_resolution() ]->[0]); my $current_mode; my $notebook = gtksignal_connect(Gtk2::Notebook->new, switch_page => sub { my (undef, undef, $mode) = @_; switch_to_mode($mode); }); $notebook->append_page(gtkpack__(Gtk2::VBox->new(0, 5), create_scale_table('px', 'pw', 'py', 'ph'), gtksignal_connect(Gtk2::Button->new(N("Choose progress bar color")), clicked => \&choose_color), gtksignal_connect(Gtk2::Button->new(N("Choose picture")), clicked => sub { choose_image('silentjpeg') })), N("Silent bootsplash")); $notebook->append_page(gtkpack__(Gtk2::VBox->new(0, 5), create_scale_table('tx', 'tw', 'ty', 'th'), gtksignal_connect(Gtk2::Button->new(N("Choose progress bar color")), clicked => \&choose_color), gtksignal_connect(Gtk2::Button->new(N("Choose picture")), clicked => sub { choose_image('jpeg') })), N("Verbose bootsplash")); $notebook->append_page(gtkpack__(Gtk2::VBox->new(0, 5), gtksignal_connect(Gtk2::CheckButton->new(N("Display logo on Console")), toggled => sub { $theme{conf}{logo} = bool2yesno($_[0]->get_active); })), N("Console bootsplash")); gtkadd($window->{window}, gtkpack(Gtk2::VBox->new(0, 5), gtkpack__(Gtk2::HBox->new(0, 5), create_packtable({ col_spacings => 10, row_spacings => 5 }, [ Gtk2::Label->new(N("Theme name")), gtksignal_connect(Gtk2::ComboBoxEntry->new_with_strings([ bootsplash::themes_list() ], $theme{name}), changed => sub { set_theme($_[0]->get_child->get_text) }) ], [ Gtk2::Label->new(N("final resolution")), gtksignal_connect(Gtk2::ComboBox->new_with_strings(\@bootsplash::resolutions, $theme{res}), changed => sub { set_resolution($_[0]->get_text) }) ])), $notebook, gtkpack(Gtk2::HBox->new(0, 5), gtksignal_connect(Gtk2::Button->new(N("Save theme")), clicked => \&save_theme), gtksignal_connect(Gtk2::Button->new(N("Quit")), clicked => \&close_all)))), gtkshow(gtkadd($preview_window->{window}, $scroll)); $window->{rwindow}->set_border_width(5); $window->{rwindow}->set_position('center'); $window->{rwindow}->show_all; $window->main; # Should never get here ugtk2->exit(0); sub close_all() { ugtk2->exit(0); } sub read_theme_config() { my $conf = bootsplash::theme_get_config_for_resolution($theme{name}, $theme{res}); -f $conf and $theme{conf} = bootsplash::theme_read_config_for_resolution($theme{name}, $theme{res}); if (-f $theme{conf}{silentjpeg}) { load_image($theme{conf}{silentjpeg}, 0, 1); } if (-f $theme{conf}{jpeg}) { load_image($theme{conf}{jpeg}, 1, 1); } $theme{conf}{pc} ||= '#21459d'; update_scale_values_from_conf(); update_rect(); } sub set_theme { my ($name) = @_; @image_pixbuf = (); $theme{name} = $name; read_theme_config(); } { my ($old_scroll_w, $old_scroll_h); sub set_resolution { my ($res) = @_; $theme{res} = $res; ($theme{res_w}, $theme{res_h}) = $theme{res} =~ /([^x]+)x([^x]+)/; update_scales_for_resolution(); read_theme_config(); $image_area->set_size_request($theme{res_w}, $theme{res_h}); #- try not to be larger than screen size minus toolbars size my $w = min(Gtk2::Gdk->screen_width*0.9, $theme{res_w}); my $h = min(Gtk2::Gdk->screen_height*0.9, $theme{res_h}); #- do not resize and move the window again if size hasn't changed if ($w != $old_scroll_w || $h != $old_scroll_h) { $scroll->set_size_request($w, $h); $preview_window->shrink_topwindow; $preview_window->{window}->move(0, 0); ($old_scroll_w, $old_scroll_h) = ($w, $h); } } } sub save_theme() { my $_w = $in->wait_message('', N("saving Bootsplash theme...")); bootsplash::theme_set_image_for_resolution($theme{name}, $theme{res}, $theme{conf}{silentjpeg}); bootsplash::theme_write_config_for_resolution($theme{name}, $theme{res}, $theme{conf}); } sub load_image { my ($img, $mode, $o_no_warn) = @_; eval { $image_pixbuf[$mode] = Gtk2::Gdk::Pixbuf->new_from_file($img) }; if ($@) { $in->ask_warn(N("Error"), N("Unable to load image file %s", $img)) unless $o_no_warn; return; } $image_pixbuf[$mode] = $image_pixbuf[$mode]->scale_simple($theme{res_w}, $theme{res_h}, 'hyper'); $image_pixbuf[1-$mode] ||= $image_pixbuf[$mode]; $image_area->queue_draw; 1; } sub choose_image { my ($type) = @_; my $file_dialog = Gtk2::FileChooserDialog->new(N("choose image"), $window->{real_window}, 'open', N("Cancel") => 'cancel', N("Ok") => 'ok'); $file_dialog->set_filename($theme{$type} || '~/'); $file_dialog->show; while ($file_dialog->run eq 'ok') { @image_pixbuf = (); if (load_image(my $img = $file_dialog->get_filename, 0)) { $theme{conf}{$type} = $img; last; } } $file_dialog->destroy; } sub choose_color() { my $color = gtkshow(Gtk2::ColorSelectionDialog->new(N("ProgressBar color selection"))); my @rgb = $theme{conf}{pc} =~ /^#(.{2})(.{2})(.{2})$/ ? (map { hex($_)*255 } ($1, $2, $3)) : (); $color->colorsel->set_current_color(gtkcolor(@rgb)); $color->cancel_button->signal_connect(clicked => sub { $color->destroy }); $color->ok_button->signal_connect(clicked => sub { my $colour = $color->colorsel->get_current_color; $theme{conf}{pc} = "#" . join('', map { sprintf("%02x", $_/255) } $colour->red, $colour->green, $colour->blue); $color->destroy; }); } #- Adjustement widgets sub create_adj_widgets() { $adj{$_} = Gtk2::Adjustment->new(0, 0, 0, 1, 10, 0) foreach keys %scale_settings; my %scale_links = (tx => 'tw', ty => 'th', px => 'pw', py => 'ph'); while (my ($n1, $n2) = each(%scale_links)) { $adj{$n1}{on_change} = $adj{$n1}->signal_connect(value_changed => sub { check_scale_value($n1, $n2); apply_scale($n1) }); $adj{$n2}{on_change} = $adj{$n2}->signal_connect(value_changed => sub { check_scale_value($n2, $n1); apply_scale($n2) }); } } sub create_scale_table { my @settings = @_; create_packtable({ col_spacings => 10, row_spacings => 5 }, map { my $w = Gtk2::HScale->new($adj{$_}); $w->set_digits(0); [ Gtk2::Label->new($scale_settings{$_}[1]), $w ]; } @settings); } sub get_scale_max { my ($name) = @_; $theme{$name =~ /[xw]$/ ? "res_w" : "res_h"}; } sub apply_scale { my ($name) = @_; $theme{conf}{$name} = $adj{$name}->get_value; update_rect(); $image_area->queue_draw; } sub check_scale_value { my ($changed, $linked) = @_; my $max = get_scale_max($changed) - $adj{$changed}->get_value; $adj{$linked}->get_value > $max and $adj{$linked}->set_value($max); } sub update_scales_for_resolution() { $theme{conf}{$_} ||= get_scale_max($_) * $scale_settings{$_}[0] foreach keys %scale_settings; $adj{$_}->upper(get_scale_max($_)) foreach keys %adj; } sub update_scale_values_from_conf() { foreach (keys %adj) { $adj{$_}->signal_handler_block($adj{$_}{on_change}); $adj{$_}->set_value($theme{conf}{$_}) ; $adj{$_}->signal_handler_unblock($adj{$_}{on_change}) }; } sub create_image_area() { my $image_area = Gtk2::DrawingArea->new; $image_area->can_focus(1); $image_area->add_events($_) foreach qw(button-press-mask pointer-motion-mask); $image_area->signal_connect(motion_notify_event => \&image_motion_notify); $image_area->signal_connect(button_press_event => \&image_button_pressed); $image_area->signal_connect(expose_event => \&image_expose); $image_area->signal_connect(key_press_event => sub { my (undef, $event) = @_; member($event->keyval, 'q', 'Escape') and close_all(); 1; }); $image_area->grab_focus; $image_area->show; $image_area; } sub image_expose { my ($widget) = @_; my $window = $widget->window; $window->draw_pixbuf($widget->style->white_gc, $image_pixbuf[$current_mode > 0], 0, 0, 0, 0, -1, -1, 'none', 0, 0) if $image_pixbuf[$current_mode > 0]; my $cross_gc; if ($current_mode == 0) { #- silent $window->draw_rectangle($widget->style->white_gc, 1, @{$theme{conf}}{'px', 'py', 'pw', 'ph'}); $cross_gc = $widget->style->black_gc; } else { #- verbose or console $window->draw_rectangle($widget->style->black_gc, 0, @{$theme{conf}}{'tx', 'ty', 'tw', 'th'}); $cross_gc = $widget->style->white_gc; } $window->draw_line($cross_gc, $current_rect->[0]{X}, $current_rect->[0]{Y}, $current_rect->[1]{X}, $current_rect->[1]{Y}); $window->draw_line($cross_gc, $current_rect->[0]{X}, $current_rect->[1]{Y}, $current_rect->[1]{X}, $current_rect->[0]{Y}); } sub update_rect() { undef $current_point; if ($current_mode == 0) { #- silent $current_rect = bootsplash::xywh2rectangle(@{$theme{conf}}{'px', 'py', 'pw', 'ph'}); } else { #- verbose or console $current_rect = bootsplash::xywh2rectangle(@{$theme{conf}}{'tx', 'ty', 'tw', 'th'}); } } sub switch_to_mode { my ($mode) = @_; $current_mode = $mode; update_rect(); $image_area->queue_draw; } sub update_theme_from_rect() { if ($current_mode == 0) { #- silent @{$theme{conf}}{'px', 'py', 'pw', 'ph'} = bootsplash::rectangle2xywh($current_rect); } else { #- verbose or console @{$theme{conf}}{'tx', 'ty', 'tw', 'th'} = bootsplash::rectangle2xywh($current_rect); } update_scale_values_from_conf(); $image_area->queue_draw; } sub image_button_pressed { my (undef, $event) = @_; my $point = { X => $event->x, Y => $event->y }; $current_point = bootsplash::nearest($point, @$current_rect); %$current_point = %$point; update_theme_from_rect(); } sub restrict_to_interval { my ($x, $min, $max) = @_; max($min, min($max, $x)); } sub image_motion_notify { my (undef, $event) = @_; member('button1-mask', @{$event->state}) or return; %$current_point = ( X => restrict_to_interval($event->x, 0, $theme{res_w}), Y => restrict_to_interval($event->y, 0, $theme{res_h}), ) if $current_point; update_theme_from_rect(); }