#!/usr/bin/perl -w
#
# Simple druid for selecting the appropriate tv_grab program,
# configuring it and placing to cron. The xmltv data file will
# be created to $HOME/.xmltv/tv_grab_<xx>.xml.
#
# This druid will print out one line of output which is the name
# of the xmltv data file. This can be used by other programs which
# want to have the user to configure the tv_grabber.
#

=pod

=head1 NAME

xmltv-druid - an XMLTV tv_grab program configurator

=head1 SYNOPSIS

B<xmltv-druid>

=head1 DESCRIPTION

I<xmltv-druid> presents the user a trivial, very simple configuration
druid for selecting the appropriate tv_grab program, lets the user
configure the tv_grab program and deploys a cron task to run this
grabber once a day.

The channel selection, i.e. tv_grab_* --configure part, is run in
a shell so that the more complex tv_grab programs also can function
nicely. For those grabbers that support apiconfig, the configuration
is done inside the druid.

In the end xmltv-druid will create a shell script, ~/.xmltv/run-tv-grab
which is set to the user's crontab and run daily at the time the user
specified. The XMLTV data is stored into ~/.xmltv/tv_grab_*.xml.

=head1 FILES

=over

=item B<$HOME/.xmltv/run-tv-grab>        This script is created by the druid and is run to update the xmltv data file.

=item B<$HOME/.xmltv/tv_grab_*.xml>      The XMLTV datafile of the particular grabber

=back

=head1 BUGS

=over

=item None known

=head1 AUTHOR

Petri Vakevainen <Petri.Vakevainen@iki.fi>

=head1 SEE ALSO

B<xmltv>(3) B<gshowtv>(1)

=cut

use strict;
use warnings;

use Glib qw(TRUE FALSE);
use Gtk2 qw/-init/;

use Gtk2::GladeXML;
use Gtk2::SimpleList;
use Gnome2;
use English;
use XML::Simple qw(:strict);

# These are known tv_grab probrams with known locations

my %known_tv_grabbers = (
		"tv_grab_au"         => "Australia",
		"tv_grab_be"         => "Belgium",
		"tv_grab_br"         => "Brazil",
		"tv_grab_br_net"     => "Brazil (NET cable service)",
		"tv_grab_ch"         => "Switzerland",
		"tv_grab_de_tvtoday" => "Germany",
		"tv_grab_dk"         => "Denmark",
		"tv_grab_ee"         => "Estonia",
		"tv_grab_es"         => "Spain",
		"tv_grab_es_laguiatv"=> "Spain (laguiatv)",
		"tv_grab_fi"         => "Finland",
		"tv_grab_fr"         => "France",
		"tv_grab_huro"       => "Hungary or Romania",
		"tv_grab_is"         => "Iceland",
		"tv_grab_it"         => "Italy",
		"tv_grab_jp"         => "Japan",
		"tv_grab_na_dd"      => "North America",
		"tv_grab_nl"         => "The Netherlands",
		"tv_grab_nl_wolf"    => "The Netherlands (wolf)",
		"tv_grab_no"         => "Norway",
		"tv_grab_re"         => "Reunion Island (France)",
		"tv_grab_ru"         => "Russia",
		"tv_grab_se"         => "Sweden",
		"tv_grab_se_swedb"   => "Sweden (swedb)",
		"tv_grab_uk_bleb"    => "The UK (bleb)",
		"tv_grab_uk_rt"      => "The UK (rt)",
		"tv_grab_pt"         => "Portugal",
		"tv_grab_za"         => "South Africa",
		"tv_grab_es_digital" => "Spain (digital)"
	       );

# Program accelerators, to preparse the xmltv files, or some other such feature to be run after tv_grab command.
# Authors of programs that use the xmltv data can request by email to be added to the list, if such a need exists.
# [command to test, title, description, runnable command]
my @program_accelerators = ( 
	[ "gshowtv", "gShowTV", "Optimize the xmltv file for gshowtv. Speeds up the startup of gshowtv by a factor of 1:100.", "gshowtv -p XMLTVFILE" ]
	);

# Container for all the program accelerators
my $accel_vbox         = undef;

# Grabber name
my $grabber             = undef;

# Set if the grabber has been configured
my $grabber_initialized = undef;

# Grabber conf first page
my $grabber_conf_page   = undef;

# Set if the cronjob has been set
my $time                = "00:00";

# Set the cronjob date where 0 - everyday and 1 - monday, 2 ...
my $date                = 0;

# Command line options for the task
my $options             = "--quiet";

# Glade instance
my $glade               = undef;

# Textentry
my $textentry           = undef;

# Progressbar
my $progressbar         = undef;

# Runbutton
my $runbutton           = undef;

# Druid
my $druid               = undef;

# Window
my $application         = undef;

# Druidpages
my ($indexpage, $locationpage, $channelspage, $timepage, $optionspage, $populatepage, $optimizepage, $finalpage);

# My configuration directory
my $dir                 = $ENV{"HOME"} . "/.xmltv";

# Is debug enabled?
my $DEBUG               = FALSE;

# For apiconfigurable tv_grabbers, use this language by default.
my $language = "en";

# Grabber configuration
my %grabber_configuration = ();

# Temporary config file for the grabber
my $config_file_tmp = "/tmp/.xmltv-druid-config.$$";

# Fetch the configuration, if the user has already configured xmltv
sub get_config {
  # Fetch the crontab entry if such exists
  my $crontabentry = `crontab -l | grep run-tv-grab`;
  chomp($crontabentry);

  if ($crontabentry ne "") {
    print "Crontab: $crontabentry\n" if ($DEBUG);

    $crontabentry =~ m/\s*(\d+)\s*(\d+)\s*(\*)\s*(\*)\s*([\*\d]+)\s*/;

    $time = $2 . ":" . $1;
    $date = $5;
    $date = 0 if ($5 eq "*")
  }

  print "Crontab parsed: $time $date\n" if ($DEBUG);

  if (-e $dir . "/run-tv-grab") {
    $grabber = `grep "GRABBER=" $dir/run-tv-grab`;
    $options = `grep "OPTIONS=" $dir/run-tv-grab`;
    chomp($grabber);
    chomp($options);
    $grabber =~ s#GRABBER="(.*)"#$1#;
    $options =~ s#OPTIONS="(.*)"#$1#;

    $grabber_initialized = 1;
    print "Grabber: $grabber $options\n" if ($DEBUG);

    $textentry->set_text($options);
  }
}

# Saves the configuration to the script and deploys it to cron
sub save_config {
  die "Cannot create run-tv-grab file\n" unless open(WRITE, ">$dir/run-tv-grab");

  $options = $textentry->get_text();

  print WRITE "#!/bin/bash\n";
  print WRITE "######################################################################\n";
  print WRITE "# DO NOT EDIT THIS FILE\n";
  print WRITE "#\n";
  print WRITE "# This is an autogenerated script by xmltv configuration druid.\n";
  print WRITE "# This script fetches the xmltv listings with the appropriate grabber.\n";
  print WRITE "# To configure, run the druid.\n";
  print WRITE "#\n";
  print WRITE "# DO NOT EDIT THIS FILE\n";
  print WRITE "\n";
  print WRITE "GRABBER=\"$grabber\"\n";
  print WRITE "OPTIONS=\"$options\"\n";
  print WRITE "\n";
  print WRITE "cd \$HOME/.xmltv\n";
  print WRITE "\n";
  print WRITE "TMP=\"/tmp/.\$(basename \$GRABBER).\$\$.xml\"\n";
  print WRITE "\n";
  print WRITE "\$GRABBER \$OPTIONS | tv_sort >\"\$TMP\"\n";
  print WRITE "mv \"\$TMP\" \"\$HOME/.xmltv/\$(basename \$GRABBER).xml\"\n";
  print WRITE "\n# Program Accelerators \n\n";
  
  foreach my $accel ($accel_vbox->get_children()) {
		if (UNIVERSAL::isa($accel, "Gtk2::CheckButton")) {
			if ($accel->get_active()) {
				my $cmd = $accel->{data}->[3];
				$cmd =~ s/XMLTVFILE/$dir\/$grabber.xml/g;
				print WRITE $cmd . "\n";
			}
		}
	}

  close WRITE;

  chmod 0755, "$dir/run-tv-grab";

  $time =~ m/(\d+):(\d+)/;

  # Convert the date value to a weekday
  my $s = $date;
  $s = "*" if ($s == 0);

  my $crontab = "$2 $1 * * $s\t$dir/run-tv-grab\t# Automatically set up by xmltv configuration druid.";

  print "Crontab: $crontab\n" if ($DEBUG);
  print "Grabber: $grabber\n" if ($DEBUG);
  print "Options: $options\n" if ($DEBUG);

  my $file = "/tmp/.$PID.crontab.txt";

  die "Cannot open temp file\n" unless open(WRITE, ">" . $file);

  print WRITE $crontab."\n";

  close WRITE;
  system("crontab -l |grep -v run-tv-grab >> $file");
  system("crontab $file");
  unlink($file);

  if (! -e $dir . "/" . basename($grabber) . ".xml") {
    if (open WRITE, ">" . $dir . "/" . basename($grabber) . ".xml") {
      close WRITE;
    }
  }
  
  if (defined ($grabber_conf_page)) {
  	# Move the tmp grabber conf to the real configuration page
  	system("mv $config_file_tmp $dir/" . $grabber . ".conf");
  } elsif (-e $config_file_tmp) {
	# Remove the tmp configfile
  	unlink $config_file_tmp;
  }
}


# Initializes the time combobox with all possible time values
sub initialize_timebox {
  my $box = $glade->get_widget("timecombobox");
  $box->signal_connect(changed => sub { $time = $_[0]->get_active_text(); });

  my $i = 0;
  for (my $h = 0; $h < 24; $h++) {
    for (my $m = 0; $m < 60; $m = $m + 15) {
      my $str = ($h < 10 ? "0" . $h : $h) . ":" . ($m < 10 ? "0". $m : $m);
      $i++;
      $box->append_text($str);

      if ($str eq $time) {
	$box->set_active($i - 1);
      }
    }
  }

  if ($time eq "00:00") {
    $box->set_active(0);
  }
  my $datebox = $glade->get_widget("datecombobox");
  $datebox->set_active($date);
  $datebox->signal_connect(changed => sub { $date = $_[0]->get_active(); });
}

# Launch the configuration terminal
sub on_button_clicked {
  print "on_button_clicked\n" if ($DEBUG);

  system("gnome-terminal --hide-menubar -title='TV Grabber configuration' -e \"$grabber --configure\"");
  $grabber_initialized=TRUE;
}

# Initializes the list of tv_grab programs
sub initialize_grabberlist {
  my $list    = Gtk2::SimpleList->new_from_treeview($glade->get_widget("grabbertreeview"),
						    "Location" => 'text',
						    "Grabber" => 'hidden');
  $list->signal_connect("cursor-changed" => sub {
			  $grabber = $_[0]->{data}[($_[0]->get_selected_indices())[0]]->[1];
			  $grabber_initialized = undef;
			  if (defined ($grabber_conf_page)) {
				  next_page_remover($grabber_conf_page);
				  read_grabber_configuration();
				  $druid->remove($grabber_conf_page);
				  $grabber_conf_page = undef;
			  }
			  print $grabber."\n" if ($DEBUG);
			}
		       );

  @{$list->{data}} = ();

  my $i = 0;

  foreach my $c (sort values %known_tv_grabbers) {
    foreach my $g (keys %known_tv_grabbers) {
      next unless ($known_tv_grabbers{$g} eq $c);
      next unless (-x "/usr/bin/$g" || -x "/usr/local/bin/$g");

      $i++;
      push @{$list->{data}}, [ $known_tv_grabbers{$g}, $g ];
      if (defined ($grabber) && $g eq $grabber) {
	$list->select($i-1);
      }
      last;
    }
  }

  # Go through the normal locations for additional
  # grabbers that this druid doesn't know about.

  foreach my $gi (sort </usr/bin/tv_grab_* /usr/local/bin/tv_grab_*>) {
    $i++;
    chomp($gi);
    $gi =~ s#.*/##;
    next if (defined($known_tv_grabbers{$gi}));

    push @{$list->{data}}, [ $gi, $gi ];
    if (defined ($grabber) && $gi eq $grabber) {
      $list->select($i-1);
    }
  }
  
  # Read grabber configuration if one is chosen
  
  if (defined ($grabber)) {
  	read_grabber_configuration();
  }
}

# Initializes the druid
sub initialize_druid {
  $druid = $glade->get_widget("druid1");

  $druid->cancel->signal_connect(clicked => sub { Gtk2->main_quit; });
  $druid->finish->signal_connect(clicked => sub {
				   save_config() ;

				   # This is the required "exit" value, to let the other applications using us
				   # to know where the xmltv data file is created.

				   print $dir . "/" . basename($grabber) . ".xml";
				   Gtk2->main_quit; 
				 });

	$indexpage    = $glade->get_widget("druidpageedge1");
	$locationpage = $glade->get_widget("druidpagestandard1");
	$channelspage = $glade->get_widget("druidpagestandard2");
	$timepage     = $glade->get_widget("druidpagestandard3");
	$optionspage  = $glade->get_widget("druidpagestandard4");
	$populatepage = $glade->get_widget("druidpagestandard5");
	$finalpage    = $glade->get_widget("druidpageedge2");

	# Allow next if grabber is chosen
	$locationpage->signal_connect(next => sub {
		return FALSE if (defined ($grabber));
		return TRUE;
	});
	# Allow next if grabber is configured
	$channelspage->signal_connect(next => sub {
		return FALSE if (defined ($grabber_initialized));
		return TRUE;
	});
	
	$druid->remove($channelspage);
	
	# Make the intelligent switch from the grabber selection page to either xterm configuration
	# or the apiconfiguration supported by some of the grabbers
  	$locationpage->signal_connect(next => sub {
  		if (test_api_config($grabber)) {
  			if (defined ($channelspage->get_parent())) {
  				$druid->remove($channelspage);
  			}
  			unless (defined ($grabber_conf_page)) {
				$grabber_conf_page = handle_stage($locationpage, $config_file_tmp, "start");
  			}
  		} else {
  			unless (defined ($channelspage->get_parent())) {
				$druid->insert_page($locationpage, $channelspage);  		
  			}
  		}
  		FALSE;
  	});
  	
  	$accel_vbox = $glade->get_widget("acceleratorvbox");
  	my $tooltip = Gtk2::Tooltips->new();
  	
  	foreach my $accel (@program_accelerators) {
  		my $c = `which $$accel[0]`;
  		chomp($c);
		if (defined ($c) && length($c) > 0 && -x $c) {
			my $button = Gtk2::CheckButton->new($$accel[1]);
			$button->set_active(TRUE);
			$tooltip->set_tip($button, $$accel[2], undef);
			$accel_vbox->add($button);
			$button->{data} = $accel;
		}
  	}
}

# Helper, like basename(1)
sub basename {
  my $s = shift;

  $s =~ s#.*/##;
  $s;
}


# Run the tv grabber to populate the xmltv file
my $timeout = undef;
sub on_run_clicked {
  $runbutton->set_sensitive(FALSE);
  save_config();

  my $pipe;
  my $tag;
  my $counter = 0;
  my $timeout = undef;

  open $pipe, '-|', "$dir/run-tv-grab" or die "Failed running tv_grab command\n";
  $druid->set_buttons_sensitive(FALSE, FALSE, FALSE, FALSE);
  $tag = Glib::IO->add_watch(fileno($pipe), 'hup', sub {
			       close( $pipe );
			       Glib::Source->remove($timeout);
			       Glib::Source->remove($tag);
			       my $parent = $progressbar->get_parent();
			       $parent->remove($progressbar);
			       $progressbar = Gtk2::ProgressBar->new();
			       $progressbar->set_fraction(1);
			       $progressbar->set_text("Done.");
			       $parent->pack_start($progressbar, TRUE, TRUE, 5);
			       $parent->reorder_child($progressbar, 0);
			       $parent->show_all();
			       $runbutton->set_sensitive(TRUE);
			       $druid->set_buttons_sensitive(TRUE, TRUE, TRUE, TRUE);
			       
			       TRUE;
			     });

  $timeout = Glib::Timeout->add(100, sub {
				  $progressbar->pulse();
				  TRUE;
				});
}

# Part of the configureapi configuration of a grabber
sub next {
	my $page = shift;

	my $next_stage = $page->{tree}->{nextstage};
	my $prev_stage = $page->{stage};
		
	print "Moving from: $prev_stage to $next_stage.\n" if ($DEBUG);
	
	if ($next_stage eq "end") {
		serialize_grabber_conf_answers($page, $config_file_tmp);
		$grabber_initialized = 1;
		return FALSE;
	}

	unless (defined ($page->{next_page})) {
		serialize_grabber_conf_answers($page, $config_file_tmp);
		my $next_page = ($page, $config_file_tmp, $next_stage);
		$next_page->{stage} = $next_stage;
		$page->{next_page} = $next_page;
		$next_page->{prev_page} = $page;
	}
	
	return FALSE;
}

# Part of the configureapi configuration of a grabber
sub prev {
	my $page = shift;
	my $prev_stage = $page->{prev_page}->{stage};
	my $stage = $page->{stage};	
	{ no warnings;
	print "Moving from $stage to $prev_stage.\n" if ($DEBUG);
	}
	return FALSE;
}

# Test if the grabber supports apiconfig
sub test_api_config {
	open (my $pipe, '-|', $grabber . " --capabilities") or die "Cannot run $grabber\n";
	my $val = FALSE;
	while (<$pipe>) {
		if (/apiconfig/) {
			$val = TRUE;
			last;
		}
	}

	close $pipe;
	$val;
}

# Write the config file for the stage that we are configuring
sub serialize_grabber_conf_answers {
	my $page = shift;
	my $config_file = shift;

	if (open (WRITE, ">" . $config_file)) {
		while (defined ($page)) {
			print "Serializing stage:" . $page->{stage} .".\n" if ($DEBUG);
			my $answers = $page->{answers};

			foreach my $key (keys %{$answers}) {
				my $val = $answers->{$key};
				if (UNIVERSAL::isa($val, "ARRAY")) {
					foreach my $v (@{$val}) {
						print WRITE "$key=$v\n";
						print "$key=$v\n" if ($DEBUG);
					}
				} else {
					print WRITE "$key=$val\n";
					print "$key=$val\n" if ($DEBUG);
				}
			}
			$page = $page->{prev_page};
		}
		close WRITE;	
	}
}

# apiconfig stage handling
sub handle_stage {
	my $prev_page   = shift;
	my $config_file = shift;
	my $stage       = shift;
		
	unless (-e $config_file) {
		if (open WRITE, ">" . $config_file) {
			close WRITE;
		}
	}
	
	open (my $pipe, '-|', $grabber . " --configure-api --stage $stage --config-file $config_file") or die "Cannot run $grabber\n";

	my $xml = "";
	
	while (<$pipe>) {
		$xml = $xml . $_;
	}	
	close $pipe;

	my $p = XMLin($xml, ForceArray => 1, KeyAttr => 0);

	my $tree = parse($p);
		
#	print Data::Dumper::Dumper($tree);
	my $page = construct_page($prev_page, $tree);
	$page->{tree} = $tree;
	$page->{stage} = $stage;
	$page;
}

# Construct the apiconfig page
sub construct_page {
	my $prev_page = shift;
	my $arg = shift;
	my $answers = ();
	my $newanswers = ();
	my $elements = scalar @{$arg->{questions}};
		
	my $page = Gnome2::DruidPageStandard->new();
	$page->set_title("Configure " . $arg->{grabber});
	my $tooltips = Gtk2::Tooltips->new();

	my @entries = ();
	
	foreach my $s (@{$arg->{questions}}) {
		my $entry = undef;

		# Create the default answer, if one does not already exist
		if (!defined ($answers->{$s->{id}})) {
			if (defined ($grabber_configuration{$s->{id}})) {
				$answers->{$s->{id}} = $grabber_configuration{$s->{id}};
			} elsif ($s->{type} eq "selectone") {
				$answers->{$s->{id}} = $s->{options}->[0]->{value};
			} elsif ($s->{type} eq "string") {
				$answers->{$s->{id}} = $s->{default};
			}
		}

		if ($s->{type} eq "string") {
			$entry = Gtk2::Entry->new();
			if (defined ($grabber_configuration{$s->{id}})) {
				$entry->set_text($grabber_configuration{$s->{id}});
			} else {
				$entry->set_text($s->{default});
			}
			$entry->set_width_chars(20);
			$entry->{id} = $s->{id};
			$entry->{page} = $page;
			$entry->signal_connect(changed => sub {
				my $entry = shift;
				my $newanswers = $entry->{page}->{newanswers};
				$newanswers->{$entry->{id}} = $entry->get_text();
				print "Answer: " . $entry->{id} . " = " . $entry->get_text() . "\n" if ($DEBUG);
				$entry->{page}->{newanswers} = $newanswers;
			});
		} elsif ($s->{type} eq "selectone") {
			$entry = Gtk2::ComboBox->new_text;
			$entry->{id} = $s->{id};
			my @strings = ();
			my $i=0;
			my $active=0;
			foreach my $option (@{$s->{options}}) {
				$entry->append_text($option->{text});
				push @strings, $option->{value};
				if (defined ($grabber_configuration{$s->{id}}) &&
					$grabber_configuration{$s->{id}} eq $option->{value}) {
					$active = $i;
				}
				$i++;
			}
			$entry->set_active($active);
			$entry->{page} = $page;
			$entry->signal_connect(changed => sub {
				my $box = shift;
				my $list = shift;
				my $newanswers = $box->{page}->{newanswers};
				$newanswers->{$entry->{id}} = $$list[$box->get_active()];
				print "Answer: " . $entry->{id} . " = " . $$list[$box->get_active()] . " (" . $entry->get_active_text() . ")\n" if ($DEBUG);
				$box->{page}->{newanswers} = $newanswers;
				
			}, \@strings);

		} elsif ($s->{type} eq "selectmany") {
			$entry = Gtk2::SimpleList->new('' => 'bool', 'Channel' => 'text', '' => 'hidden');
			$entry->{id} = $s->{id};
			$entry->set_headers_visible(FALSE);
			foreach my $option (@{$s->{options}}) {
				my $flag = FALSE;
				if (defined ($grabber_configuration{$s->{id}})) {
					my $array = $grabber_configuration{$s->{id}};
					if (UNIVERSAL::isa($array, "ARRAY")) {
						CONF: foreach my $a (@$array) {
							if ($a eq $option->{value}) {
								$flag = TRUE;
								last CONF;
							}
						}
					} elsif ($array eq $option->{value}) {
						$flag = TRUE;
					}
				}
				
				push @{$entry->{data}}, [$flag, $option->{text}, $option->{value}];
			}
			$page->signal_connect(next => sub {
				my $page = shift;
				my $druid = shift;
				my $list = shift;

				my $newanswers = $page->{newanswers};
				
				my @vals = ();
				foreach my $val (@{$list->{data}}) {
					if ($$val[0] == TRUE) {
						push @vals, $$val[2];
					}
				}
				$newanswers->{$list->{id}} = \@vals;

				print "Answer: " . $list->{id} . " = " if ($DEBUG);
				foreach my $c (@vals) { print $c ." " if ($DEBUG); }
				print "\n" if ($DEBUG);
				$page->{newanswers} = $newanswers;
			}, $entry);
			my $scrolledwindow = Gtk2::ScrolledWindow->new();
			$scrolledwindow->add_with_viewport($entry);
			$scrolledwindow->set_policy('automatic', 'automatic');
			$entry = $scrolledwindow;
		}

		my $eb = Gtk2::EventBox->new();
		$eb->add($entry);
		$tooltips->set_tip($eb, $s->{description}, undef);
		$page->append_item($s->{title}, $eb, "");
		if ($entry->isa("Gtk2::ScrolledWindow")) {
			# Make the list resize when the window is resized
			my $vbox = ($page->vbox->get_children())[0];
			$page->vbox->set_child_packing($vbox, TRUE, TRUE, 0, 'start');
			if ($vbox->isa("Gtk2::VBox")) {
				# Paranoid check that this actually is a vbox.
				$vbox->set_child_packing($eb, TRUE, TRUE, 0, 'start');
			}
		}
	}

	$page->signal_connect(next => sub {
		my $page = shift;
		my $answers    = $page->{answers};
		my $newanswers = $page->{newanswers};

		print "NEXT: copy answers:" . $page->{stage} . "\n" if ($DEBUG);
				
		foreach my $key (keys %{$newanswers}) {
			my $newanswer = delete $newanswers->{$key};
			my $oldanswer = $answers->{$key};
			
			if (!defined ($oldanswer)) {
				$answers->{$key} = $newanswer;
				next;
			}

			print "New answer: $newanswer Old answer: $oldanswer\n" if ($DEBUG);
			
			if (UNIVERSAL::isa($newanswer, "ARRAY")) {
				if (!UNIVERSAL::isa($oldanswer, "ARRAY")) {
					if ($$newanswer[0] ne $oldanswer) {
						next_page_remover($page);
						$answers->{$key} = $newanswer;
					}
				}
				elsif (compare_arrays($newanswer, $oldanswer) != 0) {
					next_page_remover($page);
					$answers->{$key} = $newanswer;
				}
				next;
			}
			
			if ($newanswer ne $oldanswer) {
				next_page_remover($page);
				$answers->{$key} = $newanswer;
				next;				
			}
		}

		print "New answers remaining:" . scalar keys (%{$newanswers}) . "\n" if ($DEBUG);
	});

	$druid->insert_page($prev_page, $page);
	$page->signal_connect(next => \&next);
	$page->signal_connect(back => \&prev);
	$page->show_all();
	$page->{tree} = $arg;
	$page->{entries} = \@entries;
	$page->{answers} = $answers;
	$page->{newanswers} = $newanswers;
	$page;
}

# Remove the apiconfig pages and the answers related to them
sub next_page_remover { 
	my $page = shift;
	{ 	no warnings;
		print "next_page_remover: " . $page->{next_page} . "\n" if ($DEBUG);
	}
	while (defined ($page->{next_page})) {
		next_page_remover($page->{next_page});
		$druid->remove($page->{next_page});
		$page->{next_page} = undef;
	}
	%grabber_configuration = ();
}

# reparse the xmllite parsed tree into a tree that I like better
sub parse {
	my $tree = shift;

	my $grabber = $tree->{'grabber'};
	my $nextstage = $tree->{'nextstage'}->[0]->{'stage'};
    my @elements = ();
    
	foreach $_ (keys %$tree) {
		if ($_ eq "string") {
			foreach my $s (@{$tree->{string}}) {
				my $ss = parse_string($s);
				$ss->{type} = "string";
				push @elements, $ss;
			}			
		}
		elsif ($_ eq "selectone") {
			foreach my $s (@{$tree->{selectone}}) {
				my $ss = parse_select($s);
				$ss->{type} = "selectone";
				push @elements, $ss;
			}
		}
		elsif ($_ eq "selectmany") {
			foreach my $s (@{$tree->{selectmany}}) {
				my $ss = parse_select($s);
				$ss->{type} = "selectmany";
				push @elements, $ss;
			}
		}
	}
	my $t;
	$t->{questions} = \@elements;
	$t->{grabber} = $grabber;
	$t->{nextstage} = $nextstage;
	$t;
}

sub parse_string {
	my $s = shift;
	my $ss;
	my $index = 0;

	for (my $j = 0; $j < scalar @{$s->{title}}; $j++) {
		$index = $j if ($s->{title}->[$j]->{lang} eq $language);
	}
	$ss->{title} = $s->{title}->[$index]->{content};
	$index = 0;
	for (my $j = 0; $j < scalar @{$s->{title}}; $j++) {
		$index = $j if ($s->{description}->[$j]->{lang} eq $language);
	}
	$ss->{description} = $s->{description}->[$index]->{content};

	$ss->{id} = $s->{id};
	$ss->{default} = $s->{default};
	$ss;
}

sub parse_select {
	my $s = shift;
	my $ss;
	my $index = 0;
				
	for (my $j = 0; $j < scalar @{$s->{title}}; $j++) {
		$index = $j if ($s->{title}->[$j]->{lang} eq $language);
	}
	$ss->{title} = $s->{title}->[$index]->{content};
	$index = 0;
	for (my $j = 0; $j < scalar @{$s->{title}}; $j++) {
		$index = $j if ($s->{description}->[$j]->{lang} eq $language);
	}
	$ss->{description} = $s->{description}->[$index]->{content};

	$ss->{id} = $s->{id};
	
	my @options;
	foreach my $option (@{$s->{option}}) {
		my $opt;
		$index = 0;
		for (my $j = 0; $j < scalar @{$option->{text}}; $j++) {
			$index = $j if ($option->{text}->[$j]->{lang} eq $language);
		}
		$opt->{text} = $option->{text}->[$index]->{content};
		$opt->{value} = $option->{value};
		push @options, $opt;
	}
	$ss->{options} = \@options;
	$ss;
}

# Compares two arrays for difference
sub compare_arrays {
	my $a = shift;
	my $b = shift;

	my @aa = sort @$a;
	my @bb = sort @$b;
		
	return 1 if (scalar @aa != scalar @bb);
	
	for (my $i=0; $i < scalar @aa; $i++) {
		return 1 if ($aa[$i] ne $bb[$i]);
	}
	return 0;
}

# Reads grabber configuration for apiconfig
sub read_grabber_configuration {
	%grabber_configuration = ();
	if (open (READ, "$dir/" . $grabber . ".conf")) {
		while (<READ>) {
			chomp();
			if (/(.*)=(.*)/) {
				my $array = $grabber_configuration{$1};
				
				if (defined ($array)) {
					unless (UNIVERSAL::isa($array, "ARRAY")) {
						my $a;
						push @$a, $array;
						$array = $a;
					}
					
					push @$array, $2;
				} else {
					$array = $2;
				}

				$grabber_configuration{$1} = $array;
				print "grabber configuration: $1 $array\n" if ($DEBUG);
			}
		}
		close READ;
	}
}

#
# Main program
#

$| = 1;

# Initialize the gnome program
Gnome2::Program->init ("XMLTVSetupDruid", "1.0");

# Get the Glade
my $xml;
{
  local $/ = undef;
  $xml = <DATA>;
}

#$glade = Gtk2::GladeXML->new("xmltv-druid.glade");
$glade = Gtk2::GladeXML->new_from_buffer($xml);

$glade->signal_autoconnect_from_package('main');

$application    = $glade->get_widget('window1');
$textentry      = $glade->get_widget("optionsentry");
$textentry->set_text($options);
$progressbar    = $glade->get_widget("progressbar");
$runbutton      = $glade->get_widget("button2");

$application->signal_connect (destroy => sub { Gtk2->main_quit; });

get_config();
initialize_druid();
initialize_grabberlist();
initialize_timebox();

$application->show_all();
Gtk2->main;

__DATA__
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">

<glade-interface>
<requires lib="gnome"/>

<widget class="GtkWindow" id="window1">
  <property name="visible">True</property>
  <property name="title" translatable="yes">XMLTV Configuration</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="default_height">400</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="decorated">True</property>
  <property name="skip_taskbar_hint">False</property>
  <property name="skip_pager_hint">False</property>
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
  <property name="focus_on_map">True</property>
  <property name="urgency_hint">False</property>

  <child>
    <widget class="GnomeDruid" id="druid1">
      <property name="border_width">4</property>
      <property name="visible">True</property>
      <property name="show_help">False</property>

      <child>
	<widget class="GnomeDruidPageEdge" id="druidpagestart1">
	  <property name="visible">True</property>
	  <property name="position">GNOME_EDGE_START</property>
	  <property name="title" translatable="yes">XMLTV Configuration</property>
	  <property name="text" translatable="yes">Welcome to XMLTV tv_grab configuration. With this druid you can set up the xmltv tv_grab task to be run automatically on the background.</property>
	  <property name="logo">gshowtv.png</property>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageStandard" id="druidpagestandard1">
	  <property name="visible">True</property>
	  <property name="title" translatable="yes">Location</property>

	  <child internal-child="vbox">
	    <widget class="GtkVBox" id="druid-vbox1">
	      <property name="border_width">16</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">6</property>

	      <child>
		<widget class="GtkVBox" id="vbox1">
		  <property name="visible">True</property>
		  <property name="homogeneous">False</property>
		  <property name="spacing">0</property>

		  <child>
		    <widget class="GtkLabel" id="label1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Select your location or the grabber</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">False</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">0</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="padding">0</property>
		      <property name="expand">False</property>
		      <property name="fill">False</property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkScrolledWindow" id="grabberscrolledwindow">
		      <property name="visible">True</property>
		      <property name="can_focus">True</property>
		      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		      <property name="shadow_type">GTK_SHADOW_IN</property>
		      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>

		      <child>
			<widget class="GtkTreeView" id="grabbertreeview">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="headers_visible">True</property>
			  <property name="rules_hint">False</property>
			  <property name="reorderable">False</property>
			  <property name="enable_search">True</property>
			  <property name="fixed_height_mode">False</property>
			  <property name="hover_selection">False</property>
			  <property name="hover_expand">False</property>
			</widget>
		      </child>
		    </widget>
		    <packing>
		      <property name="padding">0</property>
		      <property name="expand">True</property>
		      <property name="fill">True</property>
		    </packing>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">True</property>
		  <property name="fill">True</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageStandard" id="druidpagestandard2">
	  <property name="visible">True</property>
	  <property name="title" translatable="yes">Channels</property>

	  <child internal-child="vbox">
	    <widget class="GtkVBox" id="druid-vbox2">
	      <property name="border_width">16</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">6</property>

	      <child>
		<widget class="GtkVBox" id="vbox2">
		  <property name="visible">True</property>
		  <property name="homogeneous">False</property>
		  <property name="spacing">0</property>

		  <child>
		    <widget class="GtkLabel" id="label2">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Configure the grabber by clicking the button and answering to the questions asked.</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">True</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">0</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="padding">0</property>
		      <property name="expand">False</property>
		      <property name="fill">False</property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkHButtonBox" id="hbuttonbox1">
		      <property name="visible">True</property>
		      <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
		      <property name="spacing">0</property>

		      <child>
			<widget class="GtkButton" id="button1">
			  <property name="visible">True</property>
			  <property name="can_default">True</property>
			  <property name="can_focus">True</property>
			  <property name="label">gtk-execute</property>
			  <property name="use_stock">True</property>
			  <property name="relief">GTK_RELIEF_NORMAL</property>
			  <property name="focus_on_click">True</property>
			  <signal name="clicked" handler="on_button_clicked" last_modification_time="Wed, 07 Sep 2005 12:51:12 GMT"/>
			</widget>
		      </child>
		    </widget>
		    <packing>
		      <property name="padding">0</property>
		      <property name="expand">True</property>
		      <property name="fill">True</property>
		    </packing>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">True</property>
		  <property name="fill">True</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageStandard" id="druidpagestandard3">
	  <property name="visible">True</property>
	  <property name="title" translatable="yes">Periodical running</property>

	  <child internal-child="vbox">
	    <widget class="GtkVBox" id="druid-vbox3">
	      <property name="border_width">16</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">6</property>

	      <child>
		<widget class="GtkTable" id="table2">
		  <property name="visible">True</property>
		  <property name="n_rows">3</property>
		  <property name="n_columns">2</property>
		  <property name="homogeneous">False</property>
		  <property name="row_spacing">0</property>
		  <property name="column_spacing">0</property>

		  <child>
		    <widget class="GtkLabel" id="label4">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Select the time when the tv_grab process should be ran. It will be ran with cron or anacron, depending on which is installed on your system.</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">True</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">5</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="left_attach">0</property>
		      <property name="right_attach">2</property>
		      <property name="top_attach">0</property>
		      <property name="bottom_attach">1</property>
		      <property name="y_options"></property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkLabel" id="label5">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Time:</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">False</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">5</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="left_attach">0</property>
		      <property name="right_attach">1</property>
		      <property name="top_attach">1</property>
		      <property name="bottom_attach">2</property>
		      <property name="x_options">fill</property>
		      <property name="y_options"></property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkLabel" id="label7">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Weekday:</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">False</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">5</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="left_attach">0</property>
		      <property name="right_attach">1</property>
		      <property name="top_attach">2</property>
		      <property name="bottom_attach">3</property>
		      <property name="x_options">fill</property>
		      <property name="y_options"></property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkEventBox" id="eventbox1">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Select the weekday when to run the tv_grab process</property>
		      <property name="visible_window">True</property>
		      <property name="above_child">False</property>

		      <child>
			<widget class="GtkComboBox" id="datecombobox">
			  <property name="visible">True</property>
			  <property name="items" translatable="yes">Every day
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday</property>
			  <property name="add_tearoffs">False</property>
			  <property name="focus_on_click">True</property>
			</widget>
		      </child>
		    </widget>
		    <packing>
		      <property name="left_attach">1</property>
		      <property name="right_attach">2</property>
		      <property name="top_attach">2</property>
		      <property name="bottom_attach">3</property>
		      <property name="x_options">fill</property>
		      <property name="y_options">fill</property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkEventBox" id="eventbox2">
		      <property name="visible">True</property>
		      <property name="tooltip" translatable="yes">Select the time of day when to run the process</property>
		      <property name="visible_window">True</property>
		      <property name="above_child">False</property>

		      <child>
			<widget class="GtkComboBox" id="timecombobox">
			  <property name="visible">True</property>
			  <property name="items" translatable="yes"></property>
			  <property name="add_tearoffs">False</property>
			  <property name="focus_on_click">True</property>
			</widget>
		      </child>
		    </widget>
		    <packing>
		      <property name="left_attach">1</property>
		      <property name="right_attach">2</property>
		      <property name="top_attach">1</property>
		      <property name="bottom_attach">2</property>
		      <property name="x_options">fill</property>
		      <property name="y_options">fill</property>
		    </packing>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">True</property>
		  <property name="fill">True</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageStandard" id="druidpagestandard4">
	  <property name="visible">True</property>
	  <property name="title" translatable="yes">Extra Options</property>

	  <child internal-child="vbox">
	    <widget class="GtkVBox" id="druid-vbox4">
	      <property name="border_width">16</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">6</property>

	      <child>
		<widget class="GtkVBox" id="vbox4">
		  <property name="visible">True</property>
		  <property name="homogeneous">False</property>
		  <property name="spacing">0</property>

		  <child>
		    <widget class="GtkLabel" id="label3">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Enter any extra command line options you want to use when running the grabber. To see which options are supported, see the grabbers manual page.</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">True</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">5</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="padding">0</property>
		      <property name="expand">False</property>
		      <property name="fill">False</property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkEntry" id="optionsentry">
		      <property name="visible">True</property>
		      <property name="can_focus">True</property>
		      <property name="editable">True</property>
		      <property name="visibility">True</property>
		      <property name="max_length">0</property>
		      <property name="text" translatable="yes"></property>
		      <property name="has_frame">True</property>
		      <property name="invisible_char">*</property>
		      <property name="activates_default">False</property>
		    </widget>
		    <packing>
		      <property name="padding">5</property>
		      <property name="expand">False</property>
		      <property name="fill">False</property>
		    </packing>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">True</property>
		  <property name="fill">True</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageStandard" id="druidpagestandard5">
	  <property name="visible">True</property>
	  <property name="title" translatable="yes">Populate XMLTV file</property>

	  <child internal-child="vbox">
	    <widget class="GtkVBox" id="druid-vbox5">
	      <property name="border_width">16</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">6</property>

	      <child>
		<widget class="GtkVBox" id="vbox5">
		  <property name="visible">True</property>
		  <property name="homogeneous">False</property>
		  <property name="spacing">0</property>

		  <child>
		    <widget class="GtkLabel" id="label6">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Please click below to run the xmltv grabber you just configured. This populates the xmltv file and you can start using XMLTV applications right away. This may take several minutes depending on the speed of your internet connection.</property>
		      <property name="use_underline">False</property>
		      <property name="use_markup">False</property>
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
		      <property name="wrap">True</property>
		      <property name="selectable">False</property>
		      <property name="xalign">0</property>
		      <property name="yalign">0.5</property>
		      <property name="xpad">5</property>
		      <property name="ypad">5</property>
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		      <property name="width_chars">-1</property>
		      <property name="single_line_mode">False</property>
		      <property name="angle">0</property>
		    </widget>
		    <packing>
		      <property name="padding">0</property>
		      <property name="expand">False</property>
		      <property name="fill">False</property>
		    </packing>
		  </child>

		  <child>
		    <widget class="GtkHBox" id="hbox1">
		      <property name="visible">True</property>
		      <property name="homogeneous">False</property>
		      <property name="spacing">0</property>

		      <child>
			<widget class="GtkProgressBar" id="progressbar">
			  <property name="visible">True</property>
			  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
			  <property name="fraction">0</property>
			  <property name="pulse_step">0.10000000149</property>
			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
			</widget>
			<packing>
			  <property name="padding">5</property>
			  <property name="expand">True</property>
			  <property name="fill">True</property>
			</packing>
		      </child>

		      <child>
			<widget class="GtkButton" id="button2">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label">gtk-execute</property>
			  <property name="use_stock">True</property>
			  <property name="relief">GTK_RELIEF_NORMAL</property>
			  <property name="focus_on_click">True</property>
			  <signal name="clicked" handler="on_run_clicked" last_modification_time="Wed, 08 Mar 2006 12:08:53 GMT"/>
			</widget>
			<packing>
			  <property name="padding">5</property>
			  <property name="expand">False</property>
			  <property name="fill">False</property>
			</packing>
		      </child>
		    </widget>
		    <packing>
		      <property name="padding">5</property>
		      <property name="expand">True</property>
		      <property name="fill">True</property>
		    </packing>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">False</property>
		  <property name="fill">False</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageStandard" id="druidpagestandard6">
	  <property name="visible">True</property>
	  <property name="title" translatable="yes">Optimize Applications</property>

	  <child internal-child="vbox">
	    <widget class="GtkVBox" id="druid-vbox6">
	      <property name="border_width">16</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">6</property>

	      <child>
		<widget class="GtkLabel" id="label8">
		  <property name="visible">True</property>
		  <property name="label" translatable="yes">Run optimizers for these applications. An optimizer speeds up the start up process of the selected application by prehandling the data when the cron job is run.
</property>
		  <property name="use_underline">False</property>
		  <property name="use_markup">False</property>
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
		  <property name="wrap">True</property>
		  <property name="selectable">False</property>
		  <property name="xalign">0</property>
		  <property name="yalign">0.5</property>
		  <property name="xpad">5</property>
		  <property name="ypad">5</property>
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
		  <property name="width_chars">-1</property>
		  <property name="single_line_mode">False</property>
		  <property name="angle">0</property>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">False</property>
		  <property name="fill">False</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkVBox" id="acceleratorvbox">
		  <property name="visible">True</property>
		  <property name="homogeneous">False</property>
		  <property name="spacing">0</property>

		  <child>
		    <placeholder/>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">False</property>
		  <property name="fill">False</property>
		</packing>
	      </child>
	    </widget>
	  </child>
	</widget>
      </child>

      <child>
	<widget class="GnomeDruidPageEdge" id="druidpagefinish1">
	  <property name="visible">True</property>
	  <property name="position">GNOME_EDGE_FINISH</property>
	  <property name="title" translatable="yes">XMLTV Configuration</property>
	  <property name="text" translatable="yes"> You have successfully configured XMLTV Grabber </property>
	</widget>
      </child>
    </widget>
  </child>
</widget>

</glade-interface>
