#!/usr/bin/perl -w

# Qemu Launcher is a GUI front-end to QEMU computer emulator.
# Copyright (C) 2004 - 2005, Erik Meitner <emeitner@f2o.org>
# Copyright (C) 2006 - 2007, Linas Žvirblis <0x0007@gmail.com>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

use strict;

use utf8;
use encoding 'utf8';

use Gtk2;
use Gtk2::GladeXML;

use POSIX;
use Locale::gettext;

use IO::File;
#use Data::Dumper; # For debugging only!

# Locale definitions
textdomain('qemu-launcher');
bindtextdomain('qemu-launcher', '/usr/share/locale/');
setlocale(LC_MESSAGES, "");

# Constants
use constant TRUE        => 1;
use constant FALSE       => 0;
use constant DEBUG_LEVEL => 1;

# Application name and version
my $application_name = gettext('Qemu Launcher');
my $version = '1.7.4';

# Configuration directory, file, and name for default settings
my $conf_dir = $ENV{'HOME'}.'/.qemu-launcher';
my $conf_file = 'configuration';
my $defaults_name = gettext('Default settings');

# Default settings for $defaults_name
my %default_config = (
	'stopped'       => 0,
	'statefile'     => '',
	'keyboard'      => gettext('Default'),
	'args'          => '',
	'comment'       => gettext('These defaults can be modified and used as a base for new configs.'),
	'boot'          => 'c',
	'cdrom'         => '/dev/cdrom',
	'usecdrom'      => FALSE,
	'hda'           => '',
	'hdb'           => '',
	'hdc'           => '',
	'hdd'           => '',
	'fda'           => '',
	'fdb'           => '',
	'ram'           => 128,
	'initrd'        => '',
	'kernel'        => '',
	'tunscript'     => '',
	'audio'         => FALSE,
	'linuxboot'     => FALSE,
	'localtime'     => TRUE,
	'nogfx'         => FALSE,
	'fullscreen'    => FALSE,
	'snapshot'      => FALSE,
	'ifmac'         => '',
	'kernelcmd'     => '',
	'gfxtype'       => 'pcivga',	# pcivga or vga
	'sndtype'       => 'sb16',		# sb16 or es1370
	'smbdir'        => '',
	'numcpus'       => 1,
	'numnics'       => 1,
	'portredirects' => '',
	'nettype'       => [ 'usermode', 'usermode', 'usermode', 'usermode',
		'usermode', 'usermode', 'usermode', 'usermode' ],
	'ifip'          => [ '', '', '', '', '', '', '', '' ],
	'port'          => [ 1, 1, 1, 1, 1, 1, 1, 1 ],
	'ifmac'         => [ '', '', '', '', '', '', '', '' ],
	'vlan'          => [ 0, 0, 0, 0, 0, 0, 0, 0 ],
	'tunscript'     => [ '', '', '', '', '', '', '', '' ],
	'ifname'        => [ '', '', '', '', '', '', '', '' ],
	'tunfd'         => [ 0, 0, 0, 0, 0, 0, 0, 0 ],
	'log'           => FALSE,
	'logthese'      => '',	# cpu,exec,in_asm,int,op_opt,op,out_asm,pcall
	'priority'      => 0,
	'monitordev'    => '',
	'serialdev'     => '',
	'qemuctl'       => FALSE,
	'acceleration'  => 'enable',	# disable, enable, full
	'systype'       => 'x86'
);

# Default settings for main configuration
my %main_config_defaults = (
	'imagedir'      => $ENV{'HOME'},
	'qemupath'      => '/usr/bin/qemu',
	'qemuimgpath'   => '/usr/bin/qemu-img',
	'qemuctlpath'   => '/usr/bin/qemuctl',
	'prelaunchcmd'  => ''
);

# Some hashes to make lookups easier
my %boot_val_by_txt = (
	'a' => 0,
	'c' => 1,
	'd' => 2
);

my %boot_val_by_num = (
	0 => 'a',
	1 => 'c',
	2 => 'd'
);

my %boot_values_dev = (
	'a' => 'fda',
	'c' => 'hda',
	'd' => 'cd'
);

my %net_val_by_num = (
	0 => 'usermode',
	1 => 'tuntap',
	2 => 'tuntapfd',
	3 => 'tcplisten',
	4 => 'tcpfd',
	5 => 'tcpconnect',
	6 => 'multicast',
	7 => 'multicastfd',
	8 => 'dummy'
);

my %net_val_by_txt = (
	'usermode'    => 0,
	'tuntap'      => 1,
	'tuntapfd'    => 2,
	'tcplisten'   => 3,
	'tcpfd'       => 4,
	'tcpconnect'  => 5,
	'multicast'   => 6,
	'multicastfd' => 7,
	'dummy'       => 8
);

my %sys_val_by_num = (
	0 => 'x86',
	1 => 'x86_64',
	2 => 'arm',
	3 => 'armeb',
	4 => 'ppc',
	5 => 'ppc64',
	6 => 'sparc',
	7 => 'sparc64',
	8 => 'mips',
	9 => 'mipsel'
);

my %sys_val_by_txt = (
	'x86'     => 0,
	'x86_64'  => 1,
	'x86-64'  => 1,
	'arm'     => 2,
	'armeb'   => 3,
	'ppc'     => 4,
	'ppc64'   => 5,
	'sparc'   => 6,
	'sparc64' => 7,
	'mips'    => 8,
	'mipsel'  => 9
);

# What a valid vm name should look like
my $good_name_regex = '^[a-zA-Z0-9_ -]+$';
# Qemu Launcher configuration
my $main_config = NULL;
# Main Glade XML file
my $gladexml = NULL;
# Stores the list of QEMU configurations (VM's)
my @vm_list = NULL;
# Name of current configuration
my $loaded_vm_config = NULL;
# TRUE if user has passed an argument (name of VM)
my $quick_launch = FALSE;
# Index for going trough arrays, loops etc.
my $idx = 0;
# Drive we are creating image for
my $drive = NULL;

# Detect Glade XML files
my $glade_files_dir = '/usr/share/qemu-launcher';

if ( $glade_files_dir =~ m/PREFIX/)
{
	$glade_files_dir = 'glade';
}

################################################
#  Functions for maniplulating the VM configs  #
################################################

# Write given VM config to a file
# Args: filename, config to write (hashref)
sub save_vm_config_to_file
{
	my ($name, $vm_config) = @_;

	if (DEBUG_LEVEL > 1)
	{
		print "save_vm_config_to_file(): Saving config:\n".Dumper($vm_config);
	}

	my $fh = new IO::File "$conf_dir/$name", 'w';

	if( ! defined $fh )
	{
		alert_user( sprintf(gettext("Could not save configuration for %s."), $name) );
		return(FALSE);
	}

	foreach my $val ( keys %{$vm_config} )
	{
		if (
			($val eq 'portredirects' and $vm_config->{$val}) or
			($val eq 'nettype' and $vm_config->{$val}) or
			($val eq 'ifip' and $vm_config->{$val}) or
			($val eq 'port' and $vm_config->{$val}) or
			($val eq 'ifmac' and $vm_config->{$val}) or
			($val eq 'vlan' and $vm_config->{$val}) or
			($val eq 'tunscript' and $vm_config->{$val}) or
			($val eq 'ifname' and $vm_config->{$val}) or
			($val eq 'tunfd' and $vm_config->{$val})
		)
		{
			my @values;

			foreach my $value ( @{$vm_config->{$val}} )
			{
				$value =~ s/:/,/g;

				if ($value eq '')
				{
					push @values, 'null';
				}
				else
				{
					push @values, $value;
				}
			}

			my $values_string = join ';', @values;
			print $fh "$val:$values_string\n";
		}
		elsif ( defined $vm_config->{$val} )
		{
			print $fh "$val:";
			print $fh $vm_config->{$val};
			print $fh "\n";
		}
	}

	close($fh);
	return(TRUE);
}

# Load a VM config file and return a config hash
# Args: file name
sub load_vm_config
{
	my $name = shift;
	my %config = %default_config;

	my $filename = "$conf_dir/$name";
	my $fh = new IO::File $filename, 'r';

	if ( ! defined $fh )
	{
		alert_user( sprintf(gettext("Could not read configuration for %s."), $name) );
		return(FALSE);
	}

	while (<$fh>)
	{
		next if /^#/;

		if( /\s*([^:]+):(.*)$/ )
		{
			if (
				($1 eq 'portredirects') or
				($1 eq 'nettype') or
				($1 eq 'ifip') or
				($1 eq 'port') or
				($1 eq 'ifmac') or
				($1 eq 'vlan') or
				($1 eq 'tunscript') or
				($1 eq 'ifname') or
				($1 eq 'tunfd')
			)
			{
				my @values = split ';', $2;

				foreach my $value ( @values )
				{
					$value =~ s/,/:/g;

					if ($value eq 'null')
					{
						$value = '';
					}
				}

				$config{$1} = \@values;
			}
			else
			{
				$config{$1} = $2;
			}
		}
	}

	close($fh);
	$loaded_vm_config = $name;

	if ( DEBUG_LEVEL > 1 )
	{
		print "load_vm_config(): Loaded config:\n".Dumper(\%config);
	}

	return(\%config);
}

# Delete a VM config file, with ack dialog
# Args: none
sub delete_vm_config
{
	my $name = get_comboentry_val('vmnamecomboboxentry');

	if ( ask_user(sprintf(gettext("Delete '%s'?"), $name)) )
	{
		if ( unlink "$conf_dir/$name" )
		{
			my $widget = $gladexml->get_widget('vmnamecomboboxentry');
			my $pos = $widget->get_active();

			$widget->remove_text($pos);
			
			# set the combo box to the default config name and load it:
			set_combobox_val( $widget, $defaults_name );
			show_vm_config();
		}
		else
		{
			alert_user( sprintf(gettext("Could not delete %s:"), $name).$! );
		}
	}
}

# Get a list of all config files, except the main config
# Args: none
sub list_vm_configs
{
	my $dirname;
	my @names = ();

	if ( ! -d $conf_dir )
	{
		return(FALSE);
	}

	opendir ( CFGDIR, $conf_dir ) or return(FALSE);

	while ( $dirname = readdir(CFGDIR) )
	{
		next if ( $dirname =~ /^\./ );
		next if ( $dirname eq $conf_file );
		next if ( $dirname eq $defaults_name );
		push( @names, $dirname );
	}

	closedir(CFGDIR);

	@names = sort(@names);
	unshift( @names, $defaults_name );

	return(@names);
}

# Set the GUI vm config values to what is set in the given config hash
# Args: hashref to a vm config
sub push_vm_config
{
	my $config = shift;

	# Configurations #
	($gladexml->get_widget('commententry'))->
		set_text( $config->{'comment'} );

	# Disks and memory #
	($gladexml->get_widget('snapshotcheckbutton'))->
		set_active( $config->{'snapshot'} );
	($gladexml->get_widget('usecdromcheckbutton'))->
		set_active( $config->{'usecdrom'} );
	($gladexml->get_widget('bootcombobox'))->
		set_active( $boot_val_by_txt{ $config->{'boot'} } );
	($gladexml->get_widget('fdafileentry'))->
		set_text( $config->{'fda'} );
	($gladexml->get_widget('fdbfileentry'))->
		set_text( $config->{'fdb'} );
	($gladexml->get_widget('cdfileentry'))->
		set_text( $config->{'cdrom'} );
	($gladexml->get_widget('hdafileentry'))->
		set_text( $config->{'hda'} );
	($gladexml->get_widget('hdbfileentry'))->
		set_text( $config->{'hdb'} );
	($gladexml->get_widget('hdcfileentry'))->
		set_text( $config->{'hdc'} );
	($gladexml->get_widget('hddfileentry'))->
		set_text( $config->{'hdd'} );
	($gladexml->get_widget('ramspinbutton'))->
		set_value( $config->{'ram'} );

	# Linux boot #
	($gladexml->get_widget('linuxbootcheckbutton'))->
		set_active( $config->{'linuxboot'} );
	($gladexml->get_widget('kernelfileentry'))->
		set_text( $config->{'kernel'} );
	($gladexml->get_widget('initrdfileentry'))->
		set_text( $config->{'initrd'} );
	($gladexml->get_widget('kernelcmdentry'))->
		set_text( $config->{'kernelcmd'} );

	# Network #
	($gladexml->get_widget('numnicspinbutton'))->
		set_value( $config->{'numnics'} );

	# Port redirection #
	my $redirs = [];
	if ( $config->{'portredirects'} )
	{
		$redirs = $config->{'portredirects'};
	}
	push_redirs($redirs);

	($gladexml->get_widget('smbdirfileentry'))->
		set_text( $config->{'smbdir'} );

	# NICs #
	for ($idx = 0; $idx < 8; $idx++)
	{
		($gladexml->get_widget('combo_nettype_'.$idx))->
			set_active( $net_val_by_txt{$config->{'nettype'}[$idx]} );
		($gladexml->get_widget('entry_ipaddr_'.$idx))->
			set_text( $config->{'ifip'}[$idx] );
		($gladexml->get_widget('spinb_port_'.$idx))->
			set_value( $config->{'port'}[$idx] );
		($gladexml->get_widget('entry_macaddr_'.$idx))->
			set_text( $config->{'ifmac'}[$idx] );
		($gladexml->get_widget('spinb_vlan_'.$idx))->
			set_value( $config->{'vlan'}[$idx] );
		($gladexml->get_widget('fileentry_tuntapscript_'.$idx))->
			set_text( $config->{'tunscript'}[$idx] );
		($gladexml->get_widget('entry_ifname_'.$idx))->
			set_text( $config->{'ifname'}[$idx] );
		($gladexml->get_widget('spinb_fd_'.$idx))->
			set_value( $config->{'tunfd'}[$idx] );
	}

	# Hardware #
	($gladexml->get_widget('localtimecheckbutton'))->
		set_active( $config->{'localtime'} || 0 );
	($gladexml->get_widget('audiocheckbutton'))->
		set_active( $config->{'audio'} || 0 );
	($gladexml->get_widget('nogfxcheckbutton'))->
		set_active( $config->{'nogfx'} || 0 );
	($gladexml->get_widget('fullscreencheckbutton'))->
		set_active( $config->{'fullscreen'} || 0 );
	($gladexml->get_widget('numcpusspinbutton'))->
		set_value( $config->{'numcpus'} );

	if ( $config->{'gfxtype'} eq 'pcivga' )
	{
		($gladexml->get_widget('gfxpciradiobutton'))->set_active(TRUE);
	}
	elsif ( $config->{'gfxtype'} eq 'vga' )
	{
		($gladexml->get_widget('gfxvgaradiobutton'))->set_active(TRUE);
	}

	if ( $config->{'sndtype'} eq 'sb16' )
	{
		($gladexml->get_widget('sndsbradiobutton'))->set_active(TRUE);
	}
	elsif ( $config->{'sndtype'} eq 'es1370' )
	{
		($gladexml->get_widget('sndesradiobutton'))->set_active(TRUE);
	}

	my $kb;
	if ( !$config->{'keyboard'} )
	{
		$kb = gettext('Default');
	}
	else
	{
		$kb = $config->{'keyboard'};
	}
	set_combobox_val( $gladexml->get_widget('keyboardcombobox'), $kb );

	my $sdev;
	if ( !$config->{'serialdev'} )
	{
		$sdev = gettext('Default');
	}
	else
	{
		$sdev = $config->{'serialdev'};
	}
	set_combobox_val( $gladexml->get_widget('serialdevcombobox'), $sdev );

	($gladexml->get_widget('systypecombobox'))->
		set_active( $sys_val_by_txt{ $config->{'systype'} } );

	# Emulator #
	($gladexml->get_widget('prispinbutton'))->
		set_value( $config->{'priority'} );
	($gladexml->get_widget('qemuctlcheckbutton'))->
		set_active( $config->{'qemuctl'} );
	($gladexml->get_widget('stoppedcheckbutton'))->
		set_active( $config->{'stopped'} );
	($gladexml->get_widget('logcheckbutton'))->
		set_active( $config->{'log'} || 0 );

	my @logitems = split ',', $config->{'logthese'};
	foreach my $logtype ('cpu','exec','in_asm','int','op_opt','op','out_asm','pcall')
	{
		($gladexml->get_widget($logtype.'togglebutton'))->set_active(FALSE);
	}
	foreach my $logtype (@logitems)
	{
		($gladexml->get_widget($logtype.'togglebutton'))->set_active(TRUE);
	}

	if ( $config->{'acceleration'} eq 'disable' )
	{
		($gladexml->get_widget('acceloffradiobutton'))->set_active(TRUE);
	}
	elsif ( $config->{'acceleration'} eq 'enable' )
	{
		($gladexml->get_widget('accelonradiobutton'))->set_active(TRUE);
	}
	elsif ( $config->{'acceleration'} eq 'full' )
	{
		($gladexml->get_widget('accelfullradiobutton'))->set_active(TRUE);
	}

	($gladexml->get_widget('statefileentry'))->
		set_text( $config->{'statefile'} );
	($gladexml->get_widget('argsentry'))->
		set_text( $config->{'args'} );

	# What is this?
	if ( $config->{'qemuctl'} )
	{
		$gladexml->get_widget('monitordevcombobox')->set_sensitive(FALSE);
	}

	my $mdev;
	if ( !$config->{'monitordev'} )
	{
		$mdev = gettext('Default');
	}
	else
	{
		$mdev = $config->{'monitordev'};
	}
	set_combobox_val( $gladexml->get_widget('monitordevcombobox'), $mdev );
}

# Get the current config from the GUI and return it
# Args: none
sub pull_vm_config
{
	my %config;

	# Configurations #
	$config{'comment'} =
		($gladexml->get_widget('commententry'))->get_text();

	# Disks and memory #
	$config{'snapshot'} =
		($gladexml->get_widget('snapshotcheckbutton'))->get_active() || 0;
	$config{'usecdrom'} =
		($gladexml->get_widget('usecdromcheckbutton'))->get_active() || 0;

	$config{'boot'} =
		$boot_val_by_num{ ($gladexml->get_widget('bootcombobox'))->get_active() };

	$config{'fda'} =
		($gladexml->get_widget('fdafileentry'))->get_text();
	$config{'fdb'} =
		($gladexml->get_widget('fdbfileentry'))->get_text();
	$config{'hda'} =
		($gladexml->get_widget('hdafileentry'))->get_text();
	$config{'hdb'} =
		($gladexml->get_widget('hdbfileentry'))->get_text();
	$config{'hdc'} =
		($gladexml->get_widget('hdcfileentry'))->get_text();
	$config{'hdd'} =
		($gladexml->get_widget('hddfileentry'))->get_text();
	$config{'cdrom'} =
		($gladexml->get_widget('cdfileentry'))->get_text();
	$config{'ram'} =
		($gladexml->get_widget('ramspinbutton'))->get_value();

	# Linux boot #
	$config{'linuxboot'} =
		($gladexml->get_widget('linuxbootcheckbutton'))->get_active() || 0;
	$config{'kernel'} =
		($gladexml->get_widget('kernelfileentry'))->get_text();
	$config{'initrd'} =
		($gladexml->get_widget('initrdfileentry'))->get_text();
	$config{'kernelcmd'} =
		($gladexml->get_widget('kernelcmdentry'))->get_text();

	# Network #
	$config{'numnics'} =
		($gladexml->get_widget('numnicspinbutton'))->get_value();

	# Port redirection #
	my @redirs = pull_redirs();
	$config{'portredirects'} = \@redirs;

	$config{'smbdir'} =
		($gladexml->get_widget('smbdirfileentry'))->get_text();

	# NICs #
	my @combo_nettype;

	for ($idx = 0; $idx < 8; $idx++)
	{
		$combo_nettype[$idx] = $gladexml->get_widget('combo_nettype_'.$idx);
		$config{'nettype'}[$idx] =
			$net_val_by_num{ ($combo_nettype[$idx]->get_active()) };

		$config{'ifip'}[$idx] =
			($gladexml->get_widget('entry_ipaddr_'.$idx))->get_text();
		$config{'port'}[$idx] =
			($gladexml->get_widget('spinb_port_'.$idx))->get_value();
		$config{'ifmac'}[$idx] =
			($gladexml->get_widget('entry_macaddr_'.$idx))->get_text();
		$config{'vlan'}[$idx] =
			($gladexml->get_widget('spinb_vlan_'.$idx))->get_value();
		$config{'tunscript'}[$idx] =
			($gladexml->get_widget('fileentry_tuntapscript_'.$idx))->
				get_text();
		$config{'ifname'}[$idx] =
			($gladexml->get_widget('entry_ifname_'.$idx))->get_text();
		$config{'tunfd'}[$idx] =
			($gladexml->get_widget('spinb_fd_'.$idx))->get_value();
	}

	# Hardware #
	$config{'systype'} =
		$sys_val_by_num{ ($gladexml->get_widget('systypecombobox'))->get_active() };

	$config{'localtime'} =
		($gladexml->get_widget('localtimecheckbutton'))->get_active() || 0;
	$config{'audio'} =
		($gladexml->get_widget('audiocheckbutton'))->get_active() || 0;
	$config{'nogfx'} =
		($gladexml->get_widget('nogfxcheckbutton'))->get_active() || 0;
	$config{'fullscreen'} =
		($gladexml->get_widget('fullscreencheckbutton'))->get_active() || 0;
	$config{'numcpus'} =
		($gladexml->get_widget('numcpusspinbutton'))->get_value();

	if ( ($gladexml->get_widget('gfxpciradiobutton'))->get_active() )
	{
		$config{'gfxtype'} = 'pcivga';
	}
	elsif ( ($gladexml->get_widget('gfxvgaradiobutton'))->get_active() )
	{
		$config{'gfxtype'} = 'vga';
	}

	if ( ($gladexml->get_widget('sndsbradiobutton'))->get_active() )
	{
		$config{'sndtype'} = 'sb16';
	}
	elsif( ($gladexml->get_widget('sndesradiobutton'))->get_active() )
	{	
		$config{'sndtype'} = 'es1370';
	}

	my $kb = get_combobox_val('keyboardcombobox');
	if ( $kb eq gettext('Default') )
	{
		$config{'keyboard'} = '';
	}
	else
	{
		$config{'keyboard'} = $kb;
	}

	my $sdev = get_combobox_val('serialdevcombobox');
	if ( $sdev eq gettext('Default') )
	{
		$config{'serialdev'} = '';
	}
	else
	{
		$config{'serialdev'} = $sdev;
	}

	# Emulator #
	$config{'priority'} =
		($gladexml->get_widget('prispinbutton'))->get_value();
	$config{'qemuctl'} =
		($gladexml->get_widget('qemuctlcheckbutton'))->get_active() || 0;
	$config{'stopped'} =
		($gladexml->get_widget('stoppedcheckbutton'))->get_active() || 0;
	$config{'log'} =
		($gladexml->get_widget('logcheckbutton'))->get_active() || 0;

	my @logitems = ();
	foreach my $logtype ( qw/cpu exec in_asm int op_opt op out_asm pcall/ )
	{
		if ( ($gladexml->get_widget( $logtype.'togglebutton' ))->get_active() )
		{
			push( @logitems, $logtype )
		}
	}
	$config{'logthese'} = join(',',@logitems);

	if ( ($gladexml->get_widget('acceloffradiobutton'))->get_active() )
	{
		$config{'acceleration'} = 'disable';
	}
	elsif ( ($gladexml->get_widget('accelonradiobutton'))->get_active() )
	{
		$config{'acceleration'} = 'enable';
	}
		elsif ( ($gladexml->get_widget('accelfullradiobutton'))->get_active() )
	{
		$config{'acceleration'} = 'full';
	}

	$config{'statefile'} =
		($gladexml->get_widget('statefileentry'))->get_text();
	$config{'args'} =
		($gladexml->get_widget('argsentry'))->get_text();

	my $mdev = get_combobox_val('monitordevcombobox');
	if ( $mdev eq gettext('Default') )
	{
		$config{'monitordev'} = '';
	}
	else
	{
		$config{'monitordev'} = $mdev;
	}

	if (DEBUG_LEVEL > 1)
	{
		print "pull_vm_config(): Pulled config:\n".Dumper(\%config);
	}

	return(\%config);
}

# Callback to do checks before actually saving
# Args: none
sub save_vm_config
{
	my $name = get_comboentry_val('vmnamecomboboxentry');
	my $config = pull_vm_config();

	if (DEBUG_LEVEL > 1)
	{
		print "save_vm_config(): Pulled config:\n".Dumper($config);
	}

	if ( $name eq '' )
	{
		alert_user( gettext("Please enter a name for this configuration.") );
	}

	if ( check_vm_name($name) )
	{
		save_vm_config_to_file( $name, $config);

		if ( $loaded_vm_config ne $name and ! in_vm_list($name) )
		{
			add_vm_to_list($name);
			$loaded_vm_config = $name;
		}
	}

	return(TRUE);
}

# Callback for when VM list entry is selected,
# if name matches one in list, get config
# Args: none
sub show_vm_config
{
	my $widget = $gladexml->get_widget('vmnamecomboboxentry');
	my $model = $widget->get_model();
	my $iter = $widget->get_active_iter();

	if ( defined $iter )
	{
		my $name = $model->get($iter, $widget->get_text_column());
		my $config = load_vm_config( $name );

		push_vm_config($config);
	}
}

################################################
#  Functions for manipulating the main config  #
################################################

# Read main config from file
# Args: none
sub get_main_config
{
	my $fh;
	my $filename = $conf_dir.'/'.$conf_file;
	my $new_config = {};

	$fh = new IO::File $filename, 'r';

	if ( !defined $fh )
	{
		alert_user( sprintf(gettext("Could not read configuration from %s"), $filename) );
		return(FALSE);
	}

	while (<$fh>)
	{
		next if /^#/;

		if ( /\s*([^:]+):(.*)$/ )
		{
			$new_config->{$1} = $2;
		}
	}

	close($fh);

	if ( DEBUG_LEVEL > 1 )
	{
		print "get_main_config(): got:\n".Dumper($new_config);
	}

	$main_config = $new_config;

	return(TRUE);
}

# Set widgets to main config settings
# Args: none
sub set_main_config
{
	($gladexml->get_widget('imagedirfileentry'))->
		set_text( $main_config->{'imagedir'} );
	($gladexml->get_widget('qemupathfileentry'))->
		set_text( $main_config->{'qemupath'} );
	($gladexml->get_widget('qemuimgpathfileentry'))->
		set_text( $main_config->{'qemuimgpath'} );
	($gladexml->get_widget('qemuctlpathfileentry'))->
		set_text( $main_config->{'qemuctlpath'} );
	($gladexml->get_widget('prelaunchentry'))->
		set_text( $main_config->{'prelaunchcmd'} );
}

# Save main config to file
# Args: hashref to a config
sub save_main_config
{
	my $fh;
	my $filename = "$conf_dir/$conf_file";
	my $main_config = shift;

	if ( ! -d $conf_dir )
	{
		mkdir($conf_dir);
	}

	$fh = new IO::File $filename, 'w';

	if( !defined $fh )
	{
		alert_user( sprintf(gettext("Could not save configuration to %s"), $filename) );
		return(FALSE);
	}

	foreach my $var ( keys %{$main_config} )
	{
		print $fh "$var:";
		print $fh $main_config->{$var} if defined $main_config->{$var};
		print $fh "\n";
	}

	close($fh);

	return(TRUE);
}

# Get values from widgets, check values, and store in main config
# Args: none
sub apply_main_config
{
	my $error = FALSE;
	
	my $imagedir =
		($gladexml->get_widget('imagedirfileentry'))->get_text();
	my $qemupath =
		($gladexml->get_widget('qemupathfileentry'))->get_text();
	my $qemuimgpath =
		($gladexml->get_widget('qemuimgpathfileentry'))->get_text();
	my $qemuctlpath =
		($gladexml->get_widget('qemuctlpathfileentry'))->get_text();

	$main_config->{'prelaunchcmd'} =
		($gladexml->get_widget('prelaunchentry'))->get_text();

	if ( !$imagedir )
	{
		alert_user( gettext("Please enter a valid data directory.")." ".
			gettext("Settings not applied.") );
		$error = TRUE;
	}
	elsif ( ! -d $imagedir )
	{
		alert_user( sprintf(gettext("%s does not exist."), $imagedir).
			gettext("Settings not applied.") );
		$error = TRUE;
	}
	else
	{
		$main_config->{'imagedir'} = $imagedir;
	}

	if ( ( $qemupath and ! -f $qemupath ) or ! $qemupath )
	{
		alert_user( sprintf(gettext("%s does not exist."), $qemupath).
			gettext("Settings not applied.") );
		$error = TRUE;
	}
	else
	{
		$main_config->{'qemupath'} = $qemupath;
	}

	if ( ( $qemuimgpath and ! -f $qemuimgpath ) or ! $qemuimgpath )
	{
		alert_user( sprintf(gettext("%s does not exist."), $qemuimgpath).
			gettext("Settings not applied.") );
		$error = TRUE;
	}
	else
	{
		$main_config->{'qemuimgpath'} = $qemuimgpath;
	}

	if ( $qemuctlpath and ! -f $qemuctlpath )
	{
		alert_user( sprintf(gettext("%s does not exist."), $qemuctlpath).
			gettext("Settings not applied.") );
		$error=TRUE;
	}
	else
	{
		$main_config->{'qemuctlpath'} = $qemuctlpath;
	}
}

# Callback for save button.
# Args: none
sub cb_save_config
{
	apply_main_config();
	save_main_config($main_config);
}

########################
#  Big important subs  #
########################

# Run QEMU possibly using nice to set the priority
# Args: VM config to use, otherwise uses GUI settings
sub run_vm
{
	my $qcmd;
	my @qcmd_parts;
	my $config;

	if ( defined $_[0] )
	{
		$config = shift;
	}
	else
	{
		$config = pull_vm_config();

		if ( !check_config($config) )
		{
			return(FALSE);
		}
	}

	if ( $config->{'priority'} != 0 )
	{
		push @qcmd_parts, 'nice -n';
		push @qcmd_parts, $config->{'priority'};
	}

	if ( $config->{'qemuctl'} and $main_config->{'qemuctlpath'} )
	{
		push @qcmd_parts, $main_config->{'qemuctlpath'};
	}
	elsif ( $main_config->{'qemupath'} )
	{
		if ( $config->{'systype'} eq 'x86' )
		{
			push @qcmd_parts, $main_config->{'qemupath'};
		}
		elsif ( $config->{'systype'} eq 'x86-64' )
		{
			# Temporary workaround for backward compatibility
			push @qcmd_parts, $main_config->{'qemupath'}.'-system-x86_64';
		}
		else
		{
			push @qcmd_parts, $main_config->{'qemupath'}.'-system-'.$config->{'systype'};
		}

		if ( $config->{'monitordev'} )
		{
			push @qcmd_parts, '-monitor';
			push @qcmd_parts, $config->{'monitordev'};
		}
	}

	if ( $config->{'stopped'} )
	{
		push @qcmd_parts, '-S';
	}

	if ( $config->{'statefile'} )
	{
		push @qcmd_parts, '-loadvm';
		push @qcmd_parts, "'".$config->{'statefile'}."'";
	}

	if ( $config->{'linuxboot'} and $config->{'kernel'} )
	{
		push @qcmd_parts, '-kernel';
		push @qcmd_parts, "'".$config->{'kernel'}."'";

		if ( $config->{'kernelcmd'} )
		{
			push @qcmd_parts, '-append';
			push @qcmd_parts, "'".$config->{'kernelcmd'}."'";
		}

		if ( $config->{'initrd'} )
		{
			push @qcmd_parts, '-initrd';
			push @qcmd_parts, "'".$config->{'initrd'}."'";
		}
	}

	if ( $config->{'boot'} )
	{
		push @qcmd_parts, '-boot';
		push @qcmd_parts, $config->{'boot'};
	}

	if ( $config->{'snapshot'} )
	{
		push @qcmd_parts, '-snapshot';
	}

	if ( $config->{'ram'} )
	{
		push @qcmd_parts, '-m';
		push @qcmd_parts, $config->{'ram'};
	}

	foreach my $disk ( qw/fda fdb hda hdb hdd/ )
	{
		if ( $config->{$disk} )
		{
			push @qcmd_parts, "-$disk";
			push @qcmd_parts, "'".$config->{$disk}."'";
		}
	}

	if ( $config->{'usecdrom'} and $config->{'cdrom'} )
	{
		push @qcmd_parts, "-cdrom ";
		push @qcmd_parts, "'".$config->{'cdrom'}."'";
	}
	elsif ( $config->{'hdc'} )
	{
		push @qcmd_parts, "-hdc";
		push @qcmd_parts, $config->{'hdc'};
	}

	if ( $config->{'numnics'} eq 0 )
	{
		push @qcmd_parts,'-net none';
	}
	else
	{
		my $val = $config->{'numnics'};

		for ($idx = 0; $idx < $val; $idx++)
		{
			if ( $config->{'ifmac'}[$idx] )
			{
				push @qcmd_parts, '-net nic,vlan='.$config->{'vlan'}[$idx].
					',macaddr='.$config->{'ifmac'}[$idx];
			}
			else
			{
				push @qcmd_parts, '-net nic,vlan='.$config->{'vlan'}[$idx];
			}

			if ( $config->{'nettype'}[$idx] eq 'usermode' )
			{
				push @qcmd_parts, '-net user,vlan='.$config->{'vlan'}[$idx];
			}
			elsif ( $config->{'nettype'}[$idx] eq 'tuntap' )
			{
				if ( $config->{'tunscript'}[$idx] )
				{
					push @qcmd_parts, '-net tap,vlan='.$config->{'vlan'}[$idx].
						',ifname='.$config->{'ifname'}[$idx].
						',script='.$config->{'tunscript'}[$idx];
				}
				else
				{
					push @qcmd_parts, '-net tap,vlan='.$config->{'vlan'}[$idx].
						',ifname='.$config->{'ifname'}[$idx];
				}
			}
			elsif ( $config->{'nettype'}[$idx] eq 'tuntapfd' )
			{
				push @qcmd_parts, '-net tap,vlan='.$config->{'vlan'}[$idx].
					',fd='.$config->{'tunfd'}[$idx];
			}
			elsif ( $config->{'nettype'}[$idx] eq 'tcplisten' )
			{
				if ( $config->{'ifip'}[$idx] )
				{
					push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
						',listen='.$config->{'ifip'}[$idx].':'.$config->{'port'}[$idx];
				}
				else
				{
					push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
						',listen=:'.$config->{'port'}[$idx];
				}
			}
			elsif ( $config->{'nettype'}[$idx] eq 'tcpconnect' )
			{
				if ( $config->{'ifip'}[$idx] )
				{
					push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
						',connect='.$config->{'ifip'}[$idx].':'.$config->{'port'}[$idx];
				}
				else
				{
					push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
						',connect=:'.$config->{'port'}[$idx];
				}
			}
			elsif ( $config->{'nettype'}[$idx] eq 'tcpfd' )
			{
				push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
					',fd='.$config->{'tunfd'}[$idx];
			}
			elsif ( $config->{'nettype'}[$idx] eq 'multicast' )
			{
				if ( $config->{'ifip'}[$idx] )
				{
					push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
						',mcast='.$config->{'ifip'}[$idx].':'.$config->{'port'}[$idx];
				}
				else
				{
					push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
						',mcast=:'.$config->{'port'}[$idx];
				}
			}
			elsif ( $config->{'nettype'}[$idx] eq 'multicastfd' )
			{
				push @qcmd_parts, '-net socket,vlan='.$config->{'vlan'}[$idx].
					',fd='.$config->{'tunfd'}[$idx];
			}
		}

		if ( $config->{'smbdir'} )
		{
			push @qcmd_parts, '-smb';
			push @qcmd_parts, $config->{'smbdir'};
		}

		if ( $config->{'portredirects'} )
		{
			foreach my $redir ( @{$config->{'portredirects'}} )
			{
				push @qcmd_parts, '-redir';
				push @qcmd_parts, $redir;
			}
		}
	}

	if ( $config->{'localtime'} )
	{
		push @qcmd_parts, '-localtime';
	}

	if (
		$config->{'audio'} and 
		(
			( $config->{'systype'} eq 'x86' ) or
			( $config->{'systype'} eq 'x86_64' ) or
			( $config->{'systype'} eq 'x86-64' ) or
			( $config->{'systype'} eq 'ppc' ) or
			( $config->{'systype'} eq 'ppc64' )
		)
	)
	{
		push @qcmd_parts, '-soundhw';
		push @qcmd_parts, $config->{'sndtype'};
	}

	if ( $config->{'fullscreen'} )
	{
		push @qcmd_parts, '-full-screen';
	}

	if ( $config->{'keyboard'} ) {
		push @qcmd_parts, '-k';
		push @qcmd_parts, $config->{'keyboard'};
	}

	if ($config->{'numcpus'} > 1)
	{
		push @qcmd_parts, '-smp';
		push @qcmd_parts, $config->{'numcpus'} ;
	}

	if ( $config->{'nogfx'} )
	{
		push @qcmd_parts, '-nographic';
	}
	elsif ( 
		( $config->{'gfxtype'} eq 'vga' ) and (
			( $config->{'systype'} eq 'x86' ) or
			( $config->{'systype'} eq 'x86-64' ) or
			( $config->{'systype'} eq 'x86_64' )
		)
	)
	{
		push @qcmd_parts, '-std-vga';
	}

	if ( $config->{'serialdev'} )
	{
		push @qcmd_parts, '-serial';
		push @qcmd_parts, $config->{'serialdev'};
	}

	if ( $config->{'log'} and scalar($config->{'logthese'}) )
	{
		push @qcmd_parts, '-d';
		push @qcmd_parts, join( ',', ($config->{'logthese'}) );
	}

	if (
		( $config->{'acceleration'} eq 'disable' ) and
		(
			( $config->{'systype'} eq 'x86' ) or
			( $config->{'systype'} eq 'x86-64' ) or
			( $config->{'systype'} eq 'x86_64' )
		)
	)
	{
		push @qcmd_parts, '-no-kqemu';
	}
	elsif (
		( $config->{'acceleration'} eq 'full' ) and
		(
			( $config->{'systype'} eq 'x86' ) or
			( $config->{'systype'} eq 'x86-64' ) or
			( $config->{'systype'} eq 'x86_64' )
		)
	)
	{
		push @qcmd_parts, '-kernel-kqemu';
	}

	if ( $config->{'args'} )
	{
		push @qcmd_parts, $config->{'args'};
	}

	if ( !$quick_launch )
	{
		push @qcmd_parts, '&';
	}

	$qcmd = join(' ', @qcmd_parts);
	print "$qcmd\n";

	if ( !$main_config->{'prelaunchcmd'} =~ /^\s*$/ )
	{
		system( $main_config->{'prelaunchcmd'} );
	}

	system($qcmd);
}

# Create disk image
# Args: none (reads global $drive)
sub create_disk_image
{
	my $window = $gladexml->get_widget('diskimageswindow');
	my $type = NULL;

	foreach my $radiobutton ( qw/flat newcow oldcow vmware cow2/ )
	{
		if ( ($gladexml->get_widget($radiobutton.'radiobutton'))->get_active() )
		{
			$type = $radiobutton;
		}
	}

	my $size = ($gladexml->get_widget('imgsizespinbutton'))->get_value();
	my $dir  = ($gladexml->get_widget('imgdirfileentry'))->get_text();
	my $name = ($gladexml->get_widget('imgnameentry'))->get_text();
	my $orig = ($gladexml->get_widget('origimgfileentry'))->get_text();

	if ( !$dir )
	{
		alert_user( gettext("Not a valid directory for 'New image location'.") );
		return(TRUE);
	}

	if ( !$name )
	{
		alert_user( gettext("Not a valid name for 'New image name'.") );
		return(TRUE);
	}

	if ( !$orig and ( $type eq 'vmware' or $type eq 'oldcow' ) )
	{
		alert_user( gettext("Not a valid image for 'Original image'.") );
		return(TRUE);
	}

	my $error = FALSE;

	if ( $type eq 'flat' )
	{
		if ( system( $main_config->{'qemuimgpath'}.
			" create -f raw \"$dir/$name\" ${size}M") )
		{
			$error = TRUE;
		}
	}
	elsif ( $type eq 'newcow' )
	{
		if( system( $main_config->{'qemuimgpath'}.
			" create -f qcow2 \"$dir/$name\" ${size}M") )
		{
			$error = TRUE;
		}
	}
	elsif ( $type eq 'oldcow' )
	{
		if (  system( $main_config->{'qemuimgpath'}.
			" convert -f raw  \"$orig\" -O qcow2 \"$dir/$name\"") )
		{
			$error = TRUE;
		}
	}
	elsif ( $type eq 'vmware' )
	{
		if ( system( $main_config->{'qemuimgpath'}.
			" convert -f vmdk  \"$orig\" -O qcow2 \"$dir/$name\"") )
		{
			$error = TRUE;
		}
	}
	elsif ( $type eq 'cow2' )
	{
		if ( system( $main_config->{'qemuimgpath'}.
			" convert -f qcow  \"$orig\" -O qcow2 \"$dir/$name\"") )
		{
			$error = TRUE;
		}
	}

	if ( $error )
	{
		alert_user( gettext("An error occurred creating the disk image:")."\n$!");
	}
	else
	{
		my $target_fileentry = $gladexml->get_widget( $drive.'fileentry' );
		$target_fileentry->set_text("$dir/$name");
		$window->hide;
	}

	return(TRUE);
}

# Create the disk image dialog
# Args: drive name this image will be used for
sub create_disk_image_dialog
{
	$drive = shift;

	my $target_entry = $gladexml->get_widget($drive.'fileentry');
	my $window = $gladexml->get_widget('diskimageswindow');
	my $type = "";
	my $sigid = undef;

	# Set values to default
	($gladexml->get_widget('imgdirfileentry'))->set_text($main_config->{'imagedir'});
	($gladexml->get_widget('origimgfileentry'))->set_text("");
	($gladexml->get_widget('imgnameentry'))->set_text("");
	($gladexml->get_widget('imgsizespinbutton'))->set_value(1000);
	($gladexml->get_widget('newcowradiobutton'))->set_active(1);

	$window->set_transient_for( $gladexml->get_widget('mainwindow') );
	$window->show();
}

# Check the boot disk values in the given config,
# raise alerts and return FALSE if problems, else TRUE
# Args: a config hashref
sub check_config
{
	my $config = shift;
	my $name = $gladexml->get_widget('bootcombobox')->get_active_text();
	my $bootdev = $config->{'boot'};
	my $bootdevfileentry = $gladexml->get_widget( $boot_values_dev{ $bootdev }.'fileentry');
	my $bootdevpath = $bootdevfileentry->get_text();

	if ( $bootdevpath eq "" )
	{
		alert_user( sprintf(gettext("No file specified for %s."), $name) );
		return(FALSE);
	}
	elsif ( ! -e $bootdevpath )
	{
		alert_user( sprintf(gettext("Not a valid file for %s."), $name) );
		return(FALSE);
	}

	return(TRUE);
}

#####################
#  Various dialogs  #
#####################

# File chooser dialog
# Args: entry name prefix
sub select_file
{
	my ($button, @data) = @_;
	my $entry = $data[0][0];
	my $parent = $data[0][1];

	my $chooser = Gtk2::FileChooserDialog->new(
		gettext('Select a File'),
		$gladexml->get_widget($parent),
		'open',
		'gtk-cancel' => 'GTK_RESPONSE_CANCEL',
		'gtk-open' => 'GTK_RESPONSE_OK'
	);

	if ($chooser->run() eq 'ok')
	{
		my $filename = $chooser->get_filename();
		my $target_entry = $gladexml->get_widget($entry.'fileentry');
		$target_entry->set_text($filename);
	}

	$chooser->destroy();
}

sub select_file_fixme
{
	my ($button, $entry) = @_;

	my $chooser = Gtk2::FileChooserDialog->new(
		gettext('Select a File'),
		$gladexml->get_widget('mainwindow'),
		'open',
		'gtk-cancel' => 'GTK_RESPONSE_CANCEL',
		'gtk-open' => 'GTK_RESPONSE_OK'
	);

	if ($chooser->run() eq 'ok')
	{
		my $filename = $chooser->get_filename();
		my $target_entry = $gladexml->get_widget('fileentry_tuntapscript_'.$entry);
		$target_entry->set_text($filename);
	}

	$chooser->destroy();
}

# Directory chooser dialog
# args: entry name prefix
sub select_directory
{
	my ($button, @data) = @_;
	my $entry = $data[0][0];
	my $parent = $data[0][1];

	my $chooser = Gtk2::FileChooserDialog->new(
		gettext('Select a Directory'),
		$gladexml->get_widget($parent),
		'select-folder',
		'gtk-cancel' => 'GTK_RESPONSE_CANCEL',
		'gtk-open' => 'GTK_RESPONSE_OK'
	);

	if ($chooser->run() eq 'ok')
	{
		my $filename = $chooser->get_filename();
		my $target_entry = $gladexml->get_widget($entry.'fileentry');
		$target_entry->set_text($filename);
	}

	$chooser->destroy();
}

# Display a modal alert for the user
# Args: message to display
sub alert_user
{
	my $msg = shift;

	if ( DEBUG_LEVEL > 1 )
	{
		print('alert_user(): "'.$msg.'"'."\n");
	}

	if ( $quick_launch )
	{
		print($msg."\n");
	}
	else
	{
		my $dialog = $gladexml->get_widget('alertdialog');
		my $label = $gladexml->get_widget('alertdialogtext');

		$dialog->set_transient_for( $gladexml->get_widget('mainwindow') );
		$label->set_markup($msg);

		$dialog->signal_connect ('response' => sub { 
			my $self = shift;
			$self->hide;
		});

		$dialog->run();
	}
}

# Ask the user an ok/cancel question
# Args: message to display
sub ask_user
{
	my $msg = shift;

	if ( DEBUG_LEVEL > 1 )
	{
		print('ask_user(): "'.$msg.'"'."\n");
	}

	my $dialog = $gladexml->get_widget('okcanceldialog');
	my $label = $gladexml->get_widget('okcanceldialogtext');

	$dialog->set_transient_for( $gladexml->get_widget('mainwindow') );
	$label->set_text($msg);

	$dialog->signal_connect ('response' => sub { 
		my $self = shift;
		$self->hide;
	});

	if ( $dialog->run() eq 'ok' )
	{
		return(TRUE);
	}
}

#####################################
#  Handle port redirection options  #
#####################################

# Add the redir rule to the lists of redirs and
# return the new list.(skips if it exists already)
# Args: $new_redir, \@redirs
sub add_redir
{
	my $new_redir = $_[0];
	my @redirs = @{$_[1]};
	my $found;

	foreach my $redir ( @redirs )
	{
		if ( $redir eq $new_redir )
		{
			$found = 1;
			last;
		}
	}

	if (!$found)
	{
		push( @redirs, $new_redir);
	}

	return(@redirs);
}

# Changes $old_redir to $new_redir in the GUI
# Pulls list from GUI, makes change in list,
# returns new list.
# Args: $new_redir, $old_redir
sub change_redir
{
	my $new_redir = $_[0];
	my $old_redir = $_[1];
	my @redirs = pull_redirs();
	my @new_redirs;

	foreach my $redir ( @redirs )
	{
		$redir = $new_redir if( $redir eq $old_redir );
		push( @new_redirs, $redir);
	}

	return(@new_redirs);
}

# Removes $redir from redir rules list, returns new list.
# Args: $redir, \@redirs
sub del_redir
{
	my $del_redir = $_[0];
	my @redirs = @{$_[1]};
	my @new_redirs;

	foreach my $redir ( @redirs )
	{
		if ( $redir ne $del_redir )
		{
			push( @new_redirs, $redir);
		}
	}

	return(@new_redirs);
}

# Pulls redir rules from GUI and returns list
# Args: none
sub pull_redirs
{
	my $redirstreemodel = ($gladexml->get_widget('redirstreeview'))->get_model;
	my $iter = $redirstreemodel->get_iter_first;
	my @redirs;

	while( $iter )
	{
		my ( $proto, $hport, $gip, $gport) = $redirstreemodel->get($iter,(0..3));
		$iter = $redirstreemodel->iter_next($iter);
		push( @redirs,"$proto:$hport:$gip:$gport" );
	}

	return(@redirs);
}

# Clear the redirs treeview and add the items from \@redirs
# args: \@redirs
sub push_redirs
{
	my @redirs = @{$_[0]};
	my $redirstreemodel = ($gladexml->get_widget('redirstreeview'))->get_model;
	my $iter = $redirstreemodel->get_iter_first;

	if ( $iter )
	{
		while( $redirstreemodel->remove($iter) ){};
	}

	$iter = $redirstreemodel->get_iter_first;

	foreach my $redir ( @redirs )
	{
		my $listiter = $redirstreemodel->append;
		my ( $proto, $hport, $gip, $gport) = split( ':', $redir );
		$redirstreemodel->set ($listiter, 0, $proto,1, $hport, 2, $gip, 3, $gport);
	}
}

# Get the redir rule that is selected in the treeview
# returns 0 if none selected
# Args: none
sub get_selected_redir
{
	my $redirstreeview = $gladexml->get_widget('redirstreeview');
	my ($path, $focus_column) = $redirstreeview->get_cursor;

	if ( !$path )
	{
		return(0);
	}

	my $model = $redirstreeview->get_model;
	my $iter=$model->get_iter( $path );
	my ( $proto, $hport, $gip, $gport) = $model->get($iter,(0..3));

	return( "$proto:$hport:$gip:$gport" );
}

# Get the redir rule from the entry widgets.
# Args: none
sub pull_redir_rule
{
	my $guestip = $gladexml->get_widget('guestoctetentry')->get_text;
	my $proto = $gladexml->get_widget('tcpprotoradiobutton')->get_active;

	if ($proto)
	{
		$proto = 'tcp';
	}
	else
	{
		$proto = 'udp';
	}

	my $hostport =  $gladexml->get_widget('hostportspinbutton')->get_value;
	my $guestport = $gladexml->get_widget('guestportspinbutton')->get_value;

	return( "$proto:$hostport:$guestip:$guestport" );
}

# Set the values from $redir_data in the entry widgets
# Args: $redir_data
sub push_redir_rule
{
	my $redir_data = shift;
	my ( $proto, $hport, $gip, $gport) = split ':', $redir_data;

	$gladexml->get_widget('guestoctetentry')->set_text($gip);

	if($proto eq 'tcp' )
	{
		my $proto = $gladexml->
			get_widget('tcpprotoradiobutton')->set_active(1);
	}
	else
	{
		my $proto = $gladexml->
			get_widget('udpprotoradiobutton')->set_active(1);
	}

	$gladexml->get_widget('hostportspinbutton')->set_value($hport);
	$gladexml->get_widget('guestportspinbutton')->set_value($gport);
}

############################
#  Miscelaneous functions  #
############################

# Check to see if a given name is currently in the vm list
# Args: name of VM config
sub in_vm_list
{
	my $vmname = shift;

	foreach my $entry ( @vm_list)
	{
		if ( $entry eq $vmname )
		{
			return(TRUE);
		}
	}

	return(FALSE);
}

# Add a name to the vm list combobox widget
# Args: name to add
sub add_vm_to_list
{
	my $vmname = shift;
	my $combo = $gladexml->get_widget('vmnamecomboboxentry');
	$combo->append_text($vmname);
}

# Read the current value from a combobox
# Args: widget name
sub get_comboentry_val
{
	my $val;
	my $widgetname = shift;
	my $widget = $gladexml->get_widget($widgetname);
	my $model = $widget->get_model();
	my $iter = $widget->get_active_iter();

	if ( defined $iter )
	{
		$val = $model->get( $iter, $widget->get_text_column() );
	}
	else
	{
		$val = ($widget->get_child())->get_text();
	}

	return($val);
}

# Read the current value from a combobox.
# Args: widget name
sub get_combobox_val
{
	my $widgetname = shift;
	my $widget = $gladexml->get_widget($widgetname);
	my $model = $widget->get_model();
	my $iter = $widget->get_active_iter();

	if ( !$iter )
	{
		$iter = $model->get_iter_first();
	}

	my $val = $model->get($iter ,0);

	return($val);
}

# Set a combobox value to be active
# Args: $combobox, $value
sub set_combobox_val
{
	my $cb = shift;
	my $val = shift;
	my $soon_to_be_active_iter;
	my $model = $cb->get_model();
	my $iter = $model->get_iter_first;

	do
	{
		my $cur_val = $model->get($iter, 0);

		if ( $cur_val eq $val )
		{
			$soon_to_be_active_iter = $iter ;
		}
	}
	while ( !$soon_to_be_active_iter and $iter = $model->iter_next($iter) );

	if ($soon_to_be_active_iter)
	{
		$cb->set_active_iter($soon_to_be_active_iter);
	}
}

# Check a string to see if we will allow it to be used as
# a VM name. If not, issue a warning to the user and return FALSE.
# legal characters defined by: $good_name_regex
# args:
sub check_vm_name
{
	my $name = shift;

	if( $name =~ /$good_name_regex/ )
	{
		return(TRUE);
	}
	else
	{
		alert_user( gettext("Some characters in the name are not valid.\n Use only letters, numbers, -, _, and space.") );
		return(FALSE);
	}
}

# Set apropriate options in NIC tabs to sensitive or not sensitive
# Args: widget number, array of bits
sub nic_set_sensitive
{
	my ($idx, @bits) = @_;

	($gladexml->get_widget('entry_ipaddr_'.$idx))->set_sensitive($bits[0]);
	($gladexml->get_widget('spinb_port_'.$idx))->set_sensitive($bits[1]);
	($gladexml->get_widget('entry_macaddr_'.$idx))->set_sensitive($bits[2]);
	($gladexml->get_widget('spinb_vlan_'.$idx))->set_sensitive($bits[3]);
	($gladexml->get_widget('fileentry_tuntapscript_'.$idx))->set_sensitive($bits[4]);
	($gladexml->get_widget('button_tuntapscript_'.$idx))->set_sensitive($bits[5]);
	($gladexml->get_widget('entry_ifname_'.$idx))->set_sensitive($bits[6]);
	($gladexml->get_widget('spinb_fd_'.$idx))->set_sensitive($bits[7]);
}

# Set apropriate options in NIC tabs to sensitive or not sensitive
# Args: widget, number of widget
sub nic_options_sensitive
{
	my ($nettypecombo, $idx) = @_;
	my $val = $net_val_by_num{ $nettypecombo->get_active() };

	if ( $val eq 'usermode' )
	{
		nic_set_sensitive( $idx, (0,0,1,1,0,0,0,0) );
	}
	elsif ( $val eq 'tuntap' )
	{
		nic_set_sensitive( $idx, (0,0,1,1,1,1,1,0) );
	}
	elsif (
			($val eq 'tuntapfd') or
			($val eq 'tcpfd') or
			($val eq 'multicastfd')
		)
	{
		nic_set_sensitive( $idx, (0,0,1,1,0,0,0,1) );
	}
	elsif (
			($val eq 'tcplisten') or
			($val eq 'tcpconnect') or
			($val eq 'multicast')
		)
	{
		nic_set_sensitive( $idx, (1,1,1,1,0,0,0,0) );
	}
	else
	{
		nic_set_sensitive( $idx, (0,0,0,0,0,0,0,0) );
	}
}

# Set port redirection options to sensitive or not sensitive
# Args: 1 for sensitive, 0 for not sensitive
sub redir_options_sensitive
{
	my $state = shift;

	($gladexml->get_widget('smbdirfileentry'))->set_sensitive($state);
	($gladexml->get_widget('redirstreeview'))->set_sensitive($state);
	($gladexml->get_widget('rediraddbutton'))->set_sensitive($state);
	($gladexml->get_widget('redirapplybutton'))->set_sensitive($state);
	($gladexml->get_widget('redirremovebutton'))->set_sensitive($state);
	($gladexml->get_widget('tcpprotoradiobutton'))->set_sensitive($state);
	($gladexml->get_widget('udpprotoradiobutton'))->set_sensitive($state);
	($gladexml->get_widget('hostportspinbutton'))->set_sensitive($state);
	($gladexml->get_widget('guestportspinbutton'))->set_sensitive($state);
	($gladexml->get_widget('guestoctetentry'))->set_sensitive($state);
}

#################
#    Startup    #
#################

# If an argument was given, assume we are
# launching from the command line.
if ( scalar @ARGV )
{
	$quick_launch = TRUE;
}
else
{
	Gtk2->init;
	$gladexml = Gtk2::GladeXML->new( $glade_files_dir.'/qemulauncher.glade' );
}

# Create default main config if none exists
if ( ! -e "$conf_dir/$conf_file" )
{
	if( ! -e $conf_dir )
	{
		mkdir $conf_dir;
	}

	save_main_config( \%main_config_defaults );
}

if( ! get_main_config() )
{
	alert_user( gettext("There was a problem reading the main configuration. Starting with defaults.") );
	$main_config = \%main_config_defaults;
}

if ( ! $quick_launch )
{
	set_main_config();
}

@vm_list = list_vm_configs();

# Launch the qemu session specified on the command line
if ( $quick_launch )
{
	if ( in_vm_list($ARGV[0]) )
	{
		run_vm( load_vm_config($ARGV[0]) );
		exit(0);
	}
	else
	{
		alert_user( sprintf(gettext("A configuration named '%s' does not exist."), $ARGV[0]) );
		exit(1);
	}
}

# Populate the vm config list
$idx = 0;
my $defaults_idx = -1;

foreach my $name ( @vm_list )
{
	add_vm_to_list( $name );

	if ( $name eq $defaults_name )
	{
		$defaults_idx = $idx;
	}

	$idx++;
}

($gladexml->get_widget('vmnamecomboboxentry'))->set_active($defaults_idx);


if ( DEBUG_LEVEL > 1 )
{
	print "main(): Config list:\n".Dumper(\@vm_list);
}

# Create default config if none exists
if ( ! -f "$conf_dir/$defaults_name" )
{
	if ( DEBUG_LEVEL > 1 )
	{
		print "main(): Creating default config\n" ;
	}

	save_vm_config_to_file( $defaults_name, \%default_config );
}

$default_config{'comment'} = gettext($default_config{'comment'});

##############################
#  Create some more widgets  #
##############################

foreach my $item (
	gettext('Floppy A'),
	gettext('Hard disk 0'),
	gettext('CD-ROM')
)
{
	($gladexml->get_widget('bootcombobox'))->append_text($item);
}

foreach my $item (
	gettext('PC, 32-bit (x86)'),
	gettext('PC, 64-bit (x86_64)'),
	gettext('ARM, little endian (arm)'),
	gettext('ARM, big endian (armeb)'),
	gettext('PowerPC, 32-bit (ppc)'),
	gettext('PowerPC, 64-bit (ppc64)'),
	gettext('SPARC, 32-bit (sparc)'),
	gettext('SPARC, 64-bit (sparc64)'),
	gettext('MIPS, big endian (mips)'),
	gettext('MIPS, little endian (mipsel)')
)
{
	($gladexml->get_widget('systypecombobox'))->append_text($item);
}

foreach my $item ( gettext('Default'), 'vc', 'pty', 'null', 'stdio' )
{
	($gladexml->get_widget('serialdevcombobox'))->append_text($item);
	($gladexml->get_widget('monitordevcombobox'))->append_text($item);
}

foreach my $item (
	gettext('Default'),
	'ar', 'da', 'de', 'de-ch', 'en-gb', 'en-us', 'es', 'et', 'fi', 'fo',
	'fr', 'fr-be', 'fr-ca', 'fr-ch', 'hr', 'hu', 'is', 'it', 'ja', 'lt',
	'lv', 'mk', 'nl', 'nl-be', 'no', 'pl', 'pt', 'pt-br', 'ru', 'sl',
	'sv', 'th', 'tr'
)
{
	($gladexml->get_widget('keyboardcombobox'))->append_text($item);
}

$gladexml->get_widget('keyboardcombobox')->set_wrap_width(6);

for ($idx = 0; $idx < 8; $idx++)
{
	foreach my $item (
		gettext('Use the user mode network stack'),
		gettext('Open a TUN/TAP interface'),
		gettext('Use an already open TUN/TAP interface'),
		gettext('Open a listening TCP socket'),
		gettext('Use an already open TCP socket'),
		gettext('Connect to listening TCP socket'),
		gettext('Create shared VLAN via UDP multicast socket'),
		gettext('Use an already open UDP multicast socket'),
		gettext('No connection')
	)
	{
		($gladexml->get_widget('combo_nettype_'.$idx))->append_text($item);
	}
}

my $renderer  = Gtk2::CellRendererText->new;
my $column1   = Gtk2::TreeViewColumn->
	new_with_attributes (gettext("Protocol"), $renderer, "text", 0 );
my $column2   = Gtk2::TreeViewColumn->
	new_with_attributes (gettext("Host port"), $renderer, "text", 1 );
my $column3   = Gtk2::TreeViewColumn->
	new_with_attributes (gettext("Guest IP"), $renderer, "text", 2 );
my $column4   = Gtk2::TreeViewColumn->
	new_with_attributes (gettext("Guest port"), $renderer, "text", 3 );
my $liststore = Gtk2::ListStore->new ( 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::String');

my $redirstreeview = $gladexml->get_widget('redirstreeview');
$redirstreeview->set_model($liststore);
$redirstreeview->append_column($column1);
$redirstreeview->append_column($column2);
$redirstreeview->append_column($column3);
$redirstreeview->append_column($column4);

($gladexml->get_widget('abouttext'))->set_markup(
	"<span size=\"xx-large\"><b>$application_name $version</b></span>"
	."\n\n"
	.gettext("Copyright (C)")." 2004, 2005, Erik Meitner &#60;erik\@wanderings.us\&#62;"
	."\n"
	.gettext("Copyright (C)")." 2006, 2007, Linas Žvirblis &#60;0x0007\@gmail.com\&#62;"
	."\n"
	.gettext("QEMU is a trademark of Fabrice Bellard")
	."\n\n"
	.gettext("This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.")
	."\n\n"
	.gettext("This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.")
);

###########################
#  Setup signal handlers  #
###########################

my $mainwindow = $gladexml->get_widget('mainwindow'); 
$mainwindow->signal_connect('delete_event' => sub { Gtk2->main_quit; TRUE });

my $cfgapplybutton = $gladexml->get_widget('cfgapplybutton');
$cfgapplybutton->signal_connect_swapped('clicked', \&apply_main_config);

my $cfgsavebutton = $gladexml->get_widget('cfgsavebutton');
$cfgsavebutton->signal_connect_swapped('clicked', \&cb_save_config );

my $cfgcancelbutton = $gladexml->get_widget('cfgcancelbutton');
$cfgcancelbutton->signal_connect_swapped('clicked', \&set_main_config );

my $vmnamecomboboxentry = $gladexml->get_widget('vmnamecomboboxentry');
$vmnamecomboboxentry->signal_connect('changed', \&show_vm_config);

my $savevmbutton = $gladexml->get_widget('savevmbutton');
$savevmbutton->signal_connect('clicked', \&save_vm_config);

my $deletevmbutton = $gladexml->get_widget('deletevmbutton');
$deletevmbutton->signal_connect('clicked', \&delete_vm_config);

my $execvmbutton = $gladexml->get_widget('execvmbutton');
$execvmbutton->signal_connect_swapped('clicked', \&run_vm);

my $closebutton = $gladexml->get_widget('closebutton');
$closebutton->signal_connect('clicked' => sub { Gtk2->main_quit; TRUE });

my $dskimggobutton = $gladexml->get_widget('gobutton');
$dskimggobutton->signal_connect('clicked', \&create_disk_image);

foreach my $prefix ( qw/fda fdb cd hda hdb hdc hdd kernel initrd qemupath qemuimgpath qemuctlpath state/ )
{
	my $button = $gladexml->get_widget($prefix.'button');
	$button->signal_connect('clicked', \&select_file, [$prefix, 'mainwindow']);
}

foreach my $prefix ( qw/imagedir smbdir/ )
{
	my $button = $gladexml->get_widget($prefix.'button');
	$button->signal_connect('clicked', \&select_directory, [$prefix, 'mainwindow']);
}

for ($idx = 0; $idx < 8; $idx++)
{
	my $button = $gladexml->get_widget('button_tuntapscript_'.$idx);
	$button->signal_connect('clicked', \&select_file_fixme, $idx);
}

##################################
#  Deal with port redir widgets  #
##################################

my $rediraddbutton = $gladexml->get_widget('rediraddbutton');
$rediraddbutton->signal_connect ('clicked'=>sub{
	my @redirs = pull_redirs;
	my $redir = pull_redir_rule;
	@redirs = add_redir( $redir, \@redirs );
	push_redirs( \@redirs );
} );

my $redirremovebutton = $gladexml->get_widget('redirremovebutton');
$redirremovebutton->signal_connect ('clicked'=>sub{
	my @redirs = pull_redirs;
	my $redir = get_selected_redir;
	if ( $redir )
	{
		@redirs = del_redir( $redir, \@redirs );
		push_redirs( \@redirs );
	}
} );

my $redirapplybutton = $gladexml->get_widget('redirapplybutton');
$redirapplybutton->signal_connect ('clicked'=>sub{
	my $redir = pull_redir_rule;
	my $old_redir = get_selected_redir;
	if ( $old_redir )
	{
		my @redirs = change_redir( $redir, $old_redir);
		push_redirs( \@redirs );
	}
} );

$redirstreeview->signal_connect ('cursor-changed'=>sub{
	my $treeview = shift;
	my ($path, $focus_column) = $treeview->get_cursor;
	my $model = $treeview->get_model;
	my $iter=$model->get_iter( $path );
	my ( $proto, $hport, $gip, $gport) = $model->get($iter,(0..3));
	push_redir_rule( "$proto:$hport:$gip:$gport" );
} );

#####################
#  Various signals  #
#####################

my $nogfxcheckbutton = $gladexml->get_widget('nogfxcheckbutton');
$nogfxcheckbutton->signal_connect ('toggled'=>sub{
	my $state =!($nogfxcheckbutton->get_active());
	my $systypecombobox = $gladexml->get_widget('systypecombobox');

	if (
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'x86' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'x86-64' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'x86_64' )
	)
	{
		($gladexml->get_widget('gfxpciradiobutton'))->set_sensitive($state);
		($gladexml->get_widget('gfxvgaradiobutton'))->set_sensitive($state);
	}
	else
	{
		($gladexml->get_widget('gfxpciradiobutton'))->set_sensitive(0);
		($gladexml->get_widget('gfxvgaradiobutton'))->set_sensitive(0);
	}
} );

my $audiocheckbutton = $gladexml->get_widget('audiocheckbutton');
$audiocheckbutton->signal_connect ('toggled'=>sub{
	my $state =($audiocheckbutton->get_active());
	($gladexml->get_widget('sndsbradiobutton'))->set_sensitive($state);
	($gladexml->get_widget('sndesradiobutton'))->set_sensitive($state);
} );

my $numnicspinbutton = $gladexml->get_widget('numnicspinbutton');
$numnicspinbutton->signal_connect('value-changed' => sub
{
	my $val = ($numnicspinbutton->get_value_as_int());

	for ($idx = 0; $idx < $val; $idx++)
	{
		($gladexml->get_widget('vbox_nic'.$idx))->show();
	}

	for ($idx = $val; $idx < 8; $idx++)
	{
		($gladexml->get_widget('vbox_nic'.$idx))->hide();
	}

	if ($val == 0)
	{
		redir_options_sensitive(0);
		( $gladexml->get_widget('smbdirfileentry') )->set_sensitive(0);
		( $gladexml->get_widget('smbdirbutton') )->set_sensitive(0);
	}
	elsif ($val >= 1)
	{
		redir_options_sensitive(1);
		( $gladexml->get_widget('smbdirfileentry') )->set_sensitive(1);
		( $gladexml->get_widget('smbdirbutton') )->set_sensitive(1);
	}
} );

my @nettypecombo;
for ($idx = 0; $idx < 8; $idx++)
{
	$nettypecombo[$idx] = $gladexml->get_widget('combo_nettype_'.$idx);
	$nettypecombo[$idx]->signal_connect('changed' => \&nic_options_sensitive, $idx);
}

my $logcheckbutton = $gladexml->get_widget('logcheckbutton');
$logcheckbutton->signal_connect ('toggled'=>sub{
	foreach my $logtype ( qw/cpu exec in_asm int op_opt op out_asm pcall/ ){
		($gladexml->get_widget($logtype.'togglebutton'))->set_sensitive($logcheckbutton ->get_active());
	}
} );

my $linuxbootcheckbutton = $gladexml->get_widget('linuxbootcheckbutton');
$linuxbootcheckbutton->signal_connect ('toggled'=>sub{
	my $state = $linuxbootcheckbutton->get_active();
	($gladexml->get_widget('kernelcmdentry'))->set_sensitive($state);
	($gladexml->get_widget('initrdfileentry'))->set_sensitive($state);
	($gladexml->get_widget('initrdbutton'))->set_sensitive($state);
	($gladexml->get_widget('kernelfileentry'))->set_sensitive($state);
	($gladexml->get_widget('kernelbutton'))->set_sensitive($state);
	($gladexml->get_widget('bootcombobox'))->set_sensitive(! $state);
} );

my $usecdromcheckbutton = $gladexml->get_widget('usecdromcheckbutton');
$usecdromcheckbutton->signal_connect ('toggled'=>sub {
	my $state = $usecdromcheckbutton->get_active();
	($gladexml->get_widget('cdfileentry'))->set_sensitive($state);
	($gladexml->get_widget('cdbutton'))->set_sensitive($state);
	($gladexml->get_widget('hdcfileentry'))->set_sensitive(! $state);
	($gladexml->get_widget('hdcbutton'))->set_sensitive(! $state);
	($gladexml->get_widget('hdccreimgbutton'))->set_sensitive(! $state);
} );

foreach my $diskname ( qw/hda hdb hdc hdd/)
{
	my $button = $gladexml->get_widget($diskname."creimgbutton");
	$button->signal_connect ('clicked'=>sub{
		create_disk_image_dialog($diskname);
	} );
}

foreach my $diskname ( qw/fda fdb hda hdb hdc hdd cd/)
{
	($gladexml->get_widget($diskname.'fileentry'))->
		set_text($main_config->{'imagedir'});
}

my $qemuctlcheckbutton = $gladexml->get_widget('qemuctlcheckbutton');
$qemuctlcheckbutton->signal_connect ('toggled'=>sub {
	my $state = ($qemuctlcheckbutton->get_active());

	if ( $state )
	{
		set_combobox_val( $gladexml->get_widget('monitordevcombobox'), 'stdio' );

		if ( get_combobox_val('serialdevcombobox' ) eq 'stdio' )
		{
			alert_user(gettext("Serial redirection cannot use 'stdio' at the same time as the monitor. Setting serial redirection to 'Default'."));
			set_combobox_val( $gladexml->get_widget('serialdevcombobox'), 'Default' );
		}

		$gladexml->get_widget('monitordevcombobox')->set_sensitive(0);
	}
	else
	{
		$gladexml->get_widget('monitordevcombobox')->set_sensitive(1);
	}
} );

my $systypecombobox = $gladexml->get_widget('systypecombobox');
$systypecombobox->signal_connect('changed' => sub {
	my $state0 = $gladexml->get_widget('audiocheckbutton')->get_active();
	my $state1 = !$gladexml->get_widget('nogfxcheckbutton')->get_active();

	if (
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'arm' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'armeb' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'sparc' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'sparc64' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'mips' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'mipsel' )
	)
	{
		$gladexml->get_widget('audiocheckbutton')->set_sensitive(0);
		$gladexml->get_widget('sndsbradiobutton')->set_sensitive(0);
		$gladexml->get_widget('sndesradiobutton')->set_sensitive(0);
	}
	else
	{
		$gladexml->get_widget('audiocheckbutton')->set_sensitive(1);
		$gladexml->get_widget('sndsbradiobutton')->set_sensitive($state0);
		$gladexml->get_widget('sndesradiobutton')->set_sensitive($state0);
	}

	if (
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'x86' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'x86-64' ) or
		( $sys_val_by_num{ $systypecombobox->get_active() } eq 'x86_64' )
	)
	{
		$gladexml->get_widget('gfxvgaradiobutton')->set_sensitive($state1);
		$gladexml->get_widget('gfxpciradiobutton')->set_sensitive($state1);

		$gladexml->get_widget('acceloffradiobutton')->set_sensitive(1);
		$gladexml->get_widget('accelonradiobutton')->set_sensitive(1);
		$gladexml->get_widget('accelfullradiobutton')->set_sensitive(1);
	}
	else
	{
		$gladexml->get_widget('gfxvgaradiobutton')->set_sensitive(0);
		$gladexml->get_widget('gfxpciradiobutton')->set_sensitive(0);

		$gladexml->get_widget('acceloffradiobutton')->set_sensitive(0);
		$gladexml->get_widget('accelonradiobutton')->set_sensitive(0);
		$gladexml->get_widget('accelfullradiobutton')->set_sensitive(0);
	}
});

############################################
#  Signal handlers for disk images window  #
############################################

	my $diskimgwindow = $gladexml->get_widget('diskimageswindow');
	$diskimgwindow->signal_connect ('delete_event' => sub {
		my $self = shift;
		$self->hide();
		return(TRUE);
	});

	my $dskimgcancelbutton = $gladexml->get_widget('cancelbutton');
	$dskimgcancelbutton->signal_connect ('clicked' => sub {
		$diskimgwindow->hide();
		return(TRUE);
	});

	my $origimgfileentry = $gladexml->get_widget('origimgfileentry');
	my $origimgbutton = $gladexml->get_widget('origimgbutton');
	my $imgdirbutton = $gladexml->get_widget('imgdirbutton');
	my $imgsizespinbutton = $gladexml->get_widget('imgsizespinbutton');

	($gladexml->get_widget('flatradiobutton'))->
		signal_connect ('clicked' => sub
		{
			$origimgfileentry->set_sensitive(FALSE);
			$origimgbutton->set_sensitive(FALSE);
			$imgsizespinbutton->set_sensitive(TRUE);
			return(TRUE);
		});

	($gladexml->get_widget('newcowradiobutton'))->
		signal_connect ('clicked' => sub
		{
			$origimgfileentry->set_sensitive(FALSE);
			$origimgbutton->set_sensitive(FALSE);
			$imgsizespinbutton->set_sensitive(TRUE);
			return(TRUE);
		});

	($gladexml->get_widget('oldcowradiobutton'))->
		signal_connect ('clicked' => sub
		{
			$origimgfileentry->set_sensitive(TRUE);
			$origimgbutton->set_sensitive(TRUE);
			$imgsizespinbutton->set_sensitive(FALSE);
			return(TRUE);
		});

	($gladexml->get_widget('vmwareradiobutton'))->
		signal_connect ('clicked' => sub
		{
			$origimgfileentry->set_sensitive(TRUE);
			$origimgbutton->set_sensitive(TRUE);
			$imgsizespinbutton->set_sensitive(FALSE);
			return(TRUE);
		});

	($gladexml->get_widget('cow2radiobutton'))->
		signal_connect ('clicked' => sub
		{
			$origimgfileentry->set_sensitive(TRUE);
			$origimgbutton->set_sensitive(TRUE);
			$imgsizespinbutton->set_sensitive(FALSE);
			return(TRUE);
		});

	$origimgbutton->signal_connect('clicked', \&select_file, ['origimg', 'diskimageswindow']);
	$imgdirbutton->signal_connect('clicked', \&select_directory, ['imgdir', 'diskimageswindow']);


# Ready.
push_vm_config( load_vm_config($defaults_name) );
# Go!
Gtk2->main;

exit;
