#!/usr/bin/perl use strict; use lib qw(/usr/lib/libDrakX); use standalone; use common; use mygtk2 qw(gtknew); use ugtk2 qw(:create :dialogs :helpers :wrappers); use interactive; use bootsplash; my $in = 'interactive'->vnew('su'); my $global_width = 700; $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(); $ugtk2::wm_icon = 'draksplash-16'; my $window = ugtk2->new('DrakSplash', do_not_track_main_window => 1); $window->{rwindow}->signal_connect(delete_event => \&close_all); my @image_pixbuf; my $current_rect = []; my $current_point; my $preview_window = ugtk2->new(N("Create new theme"), do_not_track_main_window => 1); my $image_area = create_image_area(); my $scroll = gtknew('ScrolledWindow', child => $image_area); switch_to_mode(0); my %scale_settings = ( tb_x => [ 1/10, N("X coordinate of text box") ], tb_y => [ 1/10, N("Y coordinate of text box") ], tb_w => [ 8/10, N("Text box width") ], tb_h => [ 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") ], text_x => [ 1/10, N("X coordinate of the text") ], text_y => [ 1/10, N("Y coordinate of the text") ], transp => [ 1/10, N("Text box transparency") ], ptransp => [ 1/10, N("Progress box transparency") ], text_size => [ 1/10, N("Text size") ], ); 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(gtknew('Notebook'), switch_page => sub { my (undef, undef, $mode) = @_; switch_to_mode($mode); }); $notebook->append_page(gtknew('VBox', spacing => 5, children_tight => [ gtknew('Notebook', children => [ gtknew('Label', text => N("Progress Bar")), gtknew('VBox', spacing => 5, children_tight => [ create_scale_table('px', 'pw', 'py', 'ph', 'ptransp'), gtknew('Button', text => N("Choose progress bar color 1"), clicked => sub { choose_color('pc1') }), gtknew('Button', text => N("Choose progress bar color 2"), clicked => sub { choose_color('pc2') }), gtknew('Button', text => N("Choose progress bar background"), clicked => sub { choose_color('pbg_c') }), gtknew('HButtonBox', layout => 'edge', children_tight => [ gtknew('Label', text => N("Gradient type")), gtksignal_connect( Gtk2::ComboBox->new_with_strings([ 'vertical', 'horizontal' ], 'vertical'), changed => sub { $theme{conf}{gradient} = $_[0]->entry->get_text }), ]), ]), gtknew('Label', text => N("Text")), gtknew('VBox', spacing => 5, children_tight => [ gtknew('Button', text => N("Choose text color"), clicked => sub { choose_color('text_color') }), create_scale_table('text_x', 'text_y', 'text_size'), gtknew('Button', text => N("Choose picture"), clicked => sub { choose_image('silentjpeg') })]), ]) ]), N("Silent bootsplash")); $notebook->append_page(gtknew('VBox', spacing => 5, children_tight => [ create_scale_table('tb_x', 'tb_y', 'tb_w', 'tb_h', 'transp'), gtknew('Button', text => N("Choose text zone color"), clicked => sub { choose_color('tc') }), gtknew('Table', children => [ [ gtknew('Label', text => N("Text color")), gtksignal_connect(Gtk2::ComboBox->new_with_strings([ 0 .. 15 ], 1), changed => sub { $theme{conf}{fgcolor} = $_[0]->entry->get_text }), ], [ gtknew('Label', text => N("Background color")), gtksignal_connect(Gtk2::ComboBox->new_with_strings([ 0 .. 15 ], '0'), changed => sub { $theme{conf}{bgcolor} = $_[0]->entry->get_text }), ], ]), gtknew('Button', text => N("Choose picture"), clicked => sub { choose_image('jpeg') })]), N("Verbose bootsplash")); gtkadd($window->{window}, gtknew('VBox', spacing => 5, children_loose => [ gtknew('HBox', spacing => 5, children_tight => [ gtknew('Table', col_spacings => 10, row_spacings => 5, children => [ [ gtknew('Label', text => N("Theme name")), gtksignal_connect( Gtk2::ComboBoxEntry->new_with_strings( [ bootsplash::themes_list() ], $theme{name}), changed => sub { set_theme($_[0]->entry->get_text) }) ], [ gtknew('Label', text => N("Final resolution")), gtksignal_connect(gtknew('ComboBox', text => $theme{res}, list => \@bootsplash::resolutions), changed => sub { set_resolution($_[0]->get_text) }) ]])]), gtksignal_connect(gtknew('CheckButton', text => N("Display logo on Console")), toggled => sub { $theme{conf}{logo} = bool2yesno($_[0]->get_active); }), $notebook, gtknew('HButtonBox', layout => 'edge', children_loose => [ gtknew('Button', text => N("Save theme"), clicked => \&save_theme), gtknew('Button', text => N("Quit"), clicked => \&close_all)])])), gtkshow(gtkadd($preview_window->{window}, $scroll)); $window->{rwindow}->set_size_request($global_width, -1); $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() { if (!$theme{name}) { $in->ask_warn('', N("Please enter a theme name")); return; } elsif (! -e $theme{conf}{silentjpeg}) { $in->ask_warn('', N("Please select a splash image")); return; } 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] = gtknew('Pixbuf', 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 ($var) = @_; my $color = gtkshow(Gtk2::ColorSelectionDialog->new(N("Color selection"))); print "Current color $var: $theme{conf}{$var}\n"; my @rgb = $theme{conf}{$var} =~ /^#(.{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}{$var} = "#" . join('', map { sprintf("%02x", $_/256) } $colour->red, $colour->green, $colour->blue); $color->destroy; }); } sub create_adj_widgets() { $adj{$_} = Gtk2::Adjustment->new(0, 0, 0, 1, 10, 0) foreach keys %scale_settings; my %scale_links = (tb_x => 'tb_w', tb_y => 'tb_h', px => 'pw', py => 'ph', text_x => 'px', text_y => 'py'); 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) }); } foreach my $n (qw(transp ptransp text_size)) { $adj{$n}{on_change} = $adj{$n}->signal_connect(value_changed => sub { apply_scale($n) }); } } sub create_scale_table { my @settings = @_; gtknew('Table', col_spacings => 10, row_spacings => 5, children => [ map { my $w = gtknew('HScale', adjustment => $adj{$_}, width => 100); $w->set_digits(0); [ gtknew('WrappedLabel', width => ($global_width-150), text => formatAlaTeX($scale_settings{$_}[1])), $w ]; } @settings]); } sub get_scale_max { my ($name) = @_; if ($name =~ /size/) { return 100 } if ($name =~ /transp/) { return 255 } $theme{$name =~ /[xw]$/ ? "res_w" : "res_h"}; } sub apply_scale { my ($name) = @_; $theme{conf}{$name} = int($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}{$_} ||= int(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}}{'tb_x', 'tb_y', 'tb_w', 'tb_h'}); $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}}{'tb_x', 'tb_y', 'tb_w', 'tb_h'}); } } 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}}{'tb_x', 'tb_y', 'tb_w', 'tb_h'} = 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(); }