# 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.


sub vmconfig_window_init
{
	if ( ! $glade_vmconfig )
	{
		$glade_vmconfig = Gtk2::GladeXML->
			new( $data_dir.'/glade/ql_window_vmconfig.glade' );

		$window_vmconfig = $glade_vmconfig->get_widget('window_vmconfig');
		$window_vmconfig->set_default_size( $window_vmconfig_w, $window_vmconfig_h );
		$window_vmconfig->resize( $window_vmconfig_w, $window_vmconfig_h );
	}

	return(1);
}

sub vmconfig_window_show
{
	my $title = $_[0];

	$window_vmconfig->set_title( $title.' - '.$app_name );
	$glade_main->get_widget('toggletoolbutton_edit')->set_active(1);
	$window_vmconfig->show();

	return(1);
}

sub vmconfig_window_signals
{
	$window_vmconfig->
		signal_connect( 'delete_event' => sub
		{
			$_[0]->hide();
			$glade_main->get_widget('toggletoolbutton_edit')->set_active(0);

			return(1);
		} );

	$glade_vmconfig->get_widget('button_close')->
		signal_connect( 'clicked' => sub
		{
			$window_vmconfig->hide();
			$glade_main->get_widget('toggletoolbutton_edit')->set_active(0);

			return(1);
		} );

	$glade_vmconfig->get_widget('button_save')->
		signal_connect( 'clicked' => sub
		{
			my $item = vmconfig_getselection();

			vmconfig_pull();
			vmconfig_save( $item );
			vmconfig_populate();
			vmconfig_select( $item );

			return(1);
		} );

	foreach my $item ( keys %vmconfig_choosers )
	{
		$glade_vmconfig->get_widget( 'cancel_'.$item )->
			signal_connect( 'clicked' => sub
			{
				$glade_vmconfig->get_widget( 'chooser_'.$item )->unselect_all();

				return(1);
			} );
	}

	foreach my $item ( 'fda', 'fdb', 'hda', 'hdb', 'hdc', 'hdd', 'cdrom' )
	{
		$glade_vmconfig->get_widget( 'imgbutton_'.$item )->
			signal_connect( 'clicked' => sub
			{
				diskimg_window_show( $item );

				return(1);
			} );
	}

	# Signal definitions

	$glade_vmconfig->get_widget('checkbutton_usecdrom')->
		signal_connect('toggled' => sub
		{
			cb_checkbutton_usecdrom_toggled();
		} );

	$glade_vmconfig->get_widget('checkbutton_nogfx')->
		signal_connect ('toggled' => sub
		{
			cb_checkbutton_nogfx_toggled();
		} );

	$glade_vmconfig->get_widget('checkbutton_audio')->
		signal_connect ('toggled' => sub
		{
			cb_checkbutton_audio_toggled();
		} );

	$glade_vmconfig->get_widget('spinbutton_numnics')->
		signal_connect('value-changed' => sub
		{
			cb_spinbutton_numnics_value_changed();
		} );

	$glade_vmconfig->get_widget('combobox_systype')->
		signal_connect('changed' => sub
		{
			cb_combobox_systype_changed();
		} );

	$glade_vmconfig->get_widget('combobox_nettype0')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(0);
		} );
	$glade_vmconfig->get_widget('combobox_nettype1')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(1);
		} );
	$glade_vmconfig->get_widget('combobox_nettype2')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(2);
		} );
	$glade_vmconfig->get_widget('combobox_nettype3')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(3);
		} );
	$glade_vmconfig->get_widget('combobox_nettype4')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(4);
		} );
	$glade_vmconfig->get_widget('combobox_nettype5')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(5);
		} );
	$glade_vmconfig->get_widget('combobox_nettype6')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(6);
		} );
	$glade_vmconfig->get_widget('combobox_nettype7')->
		signal_connect('changed' => sub
		{
			cb_combobox_nettype_changed(7);
		} );

	$glade_vmconfig->get_widget('checkbutton_smbserver')->
		signal_connect ('toggled' => sub
		{
			cb_checkbutton_smbserver_toggled();
		} );

	$glade_vmconfig->get_widget('checkbutton_tftpserver')->
		signal_connect('toggled' => sub
		{
			cb_checkbutton_tftpserver_toggled();
		} );

	$glade_vmconfig->get_widget('checkbutton_linuxboot')->
		signal_connect('toggled' => sub
		{
			cb_checkbutton_linuxboot_toggled();
		} );

	$glade_vmconfig->get_widget('checkbutton_log')->
		signal_connect('toggled' => sub
		{
			cb_checkbutton_log_toggled();
		} );

	$glade_vmconfig->get_widget('button_rediradd')->
		signal_connect('clicked' => sub
		{
			cb_button_rediradd_clicked();
		} );

	$glade_vmconfig->get_widget('button_redirremove')->
		signal_connect('clicked' => sub
		{
			cb_button_redirremove_clicked();
		} );

	$glade_vmconfig->get_widget('button_redirapply')->
		signal_connect ('clicked' => sub
		{
			cb_button_redirapply_clicked();
		} );

	$glade_vmconfig->get_widget('treeview_redirs')->get_selection()->
		signal_connect( 'changed' => sub
		{
			cb_treeview_redirs_slection_changed();
		} );

	# Callback functions

	sub cb_checkbutton_usecdrom_toggled
	{
		my $state =
			$glade_vmconfig->
				get_widget('checkbutton_usecdrom')->get_active();

		$glade_vmconfig->
			get_widget('chooser_cdrom')->set_sensitive( $state );
		$glade_vmconfig->
			get_widget('cancel_cdrom')->set_sensitive( $state );
		$glade_vmconfig->
			get_widget('imgbutton_cdrom')->set_sensitive( $state );

		$glade_vmconfig->
			get_widget('chooser_hdc')->set_sensitive( ! $state );
		$glade_vmconfig->
			get_widget('cancel_hdc')->set_sensitive( ! $state );
		$glade_vmconfig->
			get_widget('imgbutton_hdc')->set_sensitive( ! $state );

		return(1);
	}

	sub cb_checkbutton_nogfx_toggled
	{
		my $state =
			! $glade_vmconfig->get_widget('checkbutton_nogfx')->get_active();
		my $combobox_systype =
			$glade_vmconfig->get_widget('combobox_systype');

		if (
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'x86' ) or
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'x86_64' )
		)
		{
			$glade_vmconfig->
				get_widget('radiobutton_gfxpci')->set_sensitive( $state );
			$glade_vmconfig->
				get_widget('radiobutton_gfxvga')->set_sensitive( $state );
		}
		else
		{
			$glade_vmconfig->
				get_widget('radiobutton_gfxpci')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_gfxvga')->set_sensitive(0);
		}

		return(1);
	}

	sub cb_checkbutton_audio_toggled
	{
		my $state =
			$glade_vmconfig->get_widget('checkbutton_audio')->get_active();

		$glade_vmconfig->
			get_widget('radiobutton_sndsb')->set_sensitive( $state );
		$glade_vmconfig->
			get_widget('radiobutton_sndes')->set_sensitive( $state );

		return(1);
	}

	sub cb_spinbutton_numnics_value_changed
	{
		my $val =
			$glade_vmconfig->
				get_widget('spinbutton_numnics')->get_value_as_int();

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

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

		if ( $val == 0 )
		{
			#redir_options_sensitive(0);
			$glade_vmconfig->get_widget('notebook_nics')->hide();
			$glade_vmconfig->get_widget('dirchooser_smb')->set_sensitive(0);
			$glade_vmconfig->get_widget('dirchooser_tftp')->set_sensitive(0);
		}
		elsif ( $val == 1 )
		{
			#redir_options_sensitive(1);
			$glade_vmconfig->get_widget('notebook_nics')->show();
			$glade_vmconfig->get_widget('notebook_nics')->set_show_tabs(0);
			$glade_vmconfig->get_widget('dirchooser_smb')->set_sensitive(1);
			$glade_vmconfig->get_widget('dirchooser_tftp')->set_sensitive(1);
		}
		elsif ( $val > 1 )
		{
			#redir_options_sensitive(1);
			$glade_vmconfig->get_widget('notebook_nics')->show();
			$glade_vmconfig->get_widget('notebook_nics')->set_show_tabs(1);
			$glade_vmconfig->get_widget('dirchooser_smb')->set_sensitive(1);
			$glade_vmconfig->get_widget('dirchooser_tftp')->set_sensitive(1);
		}

		return(1);
	}

	sub cb_combobox_nettype_changed
	{
		$idx = $_[0];

		my $val =
			$net_val_by_num{ $glade_vmconfig->
				get_widget('combobox_nettype'.$idx)->get_active() };

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

		return(1);
	}

	sub cb_combobox_systype_changed
	{
		my $combobox_systype =
			$glade_vmconfig->get_widget('combobox_systype');
		my $state_audio =
			$glade_vmconfig->get_widget('checkbutton_audio')->get_active();
		my $state_nogfx =
			! $glade_vmconfig->get_widget('checkbutton_nogfx')->get_active();

		# Note to self: move these to a separate sub.
		if (
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'arm' ) or
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'armeb' ) or
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'sparc' ) or
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'sparc64' ) or
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'mips' ) or
			( $sys_val_by_num{ $combobox_systype->get_active() } eq 'mipsel' )
		)
		{
			$glade_vmconfig->
				get_widget('checkbutton_audio')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_sndsb')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_sndes')->set_sensitive(0);
		}
		else
		{
			$glade_vmconfig->
				get_widget('checkbutton_audio')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_sndsb')->set_sensitive( $state_audio );
			$glade_vmconfig->
				get_widget('radiobutton_sndes')->set_sensitive( $state_audio );
		}

		# And also these.
		if ( $sys_val_by_num{ $combobox_systype->get_active() } eq 'x86' )
		{
			$glade_vmconfig->
				get_widget('radiobutton_gfxvga')->set_sensitive( $state_nogfx );
			$glade_vmconfig->
				get_widget('radiobutton_gfxpci')->set_sensitive( $state_nogfx );

			$glade_vmconfig->
				get_widget('radiobutton_acceloff')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_accelon')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_accelfull')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_accelkvm')->set_sensitive(0);
		}
		elsif ( $sys_val_by_num{ $combobox_systype->get_active() } eq 'x86_64' )
		{
			$glade_vmconfig->
				get_widget('radiobutton_gfxvga')->set_sensitive( $state_nogfx );
			$glade_vmconfig->
				get_widget('radiobutton_gfxpci')->set_sensitive( $state_nogfx );

			$glade_vmconfig->
				get_widget('radiobutton_acceloff')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_accelon')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_accelfull')->set_sensitive(1);
			$glade_vmconfig->
				get_widget('radiobutton_accelkvm')->set_sensitive(1);
		}
		else
		{
			$glade_vmconfig->
				get_widget('radiobutton_gfxvga')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_gfxpci')->set_sensitive(0);

			$glade_vmconfig->
				get_widget('radiobutton_acceloff')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_accelon')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_accelfull')->set_sensitive(0);
			$glade_vmconfig->
				get_widget('radiobutton_accelkvm')->set_sensitive(0);
		}

		return(1);
	}

	sub cb_checkbutton_smbserver_toggled
	{
		my $state =
			$glade_vmconfig->get_widget('checkbutton_smbserver')->get_active();

		$glade_vmconfig->
			get_widget('dirchooser_smb')->set_sensitive( $state );

		return(1);
	}

	sub cb_checkbutton_tftpserver_toggled
	{
		my $state =
			$glade_vmconfig->get_widget('checkbutton_tftpserver')->get_active();

		$glade_vmconfig->
			get_widget('dirchooser_tftp')->set_sensitive( $state );

		return(1);
	}

	sub cb_checkbutton_linuxboot_toggled
	{
		my $state =
			$glade_vmconfig->get_widget('checkbutton_linuxboot')->get_active();

		$glade_vmconfig->
			get_widget('chooser_kernel')->set_sensitive( $state );
		$glade_vmconfig->
			get_widget('chooser_initrd')->set_sensitive( $state );
		$glade_vmconfig->
			get_widget('entry_kernelcmd')->set_sensitive( $state );

		return(1);
	}

	sub cb_checkbutton_log_toggled
	{
		my $state =
			$glade_vmconfig->get_widget('checkbutton_log')->get_active();

		foreach my $item (
			'log_cpu',
			'log_exec',
			'log_in_asm',
			'log_int',
			'log_op_opt',
			'log_op',
			'log_out_asm',
			'log_pcall'
		)
		{
			$glade_vmconfig->
				get_widget('togglebutton_'.$item)->set_sensitive( $state );
		}

		return(1);
	}

	sub cb_button_rediradd_clicked
	{
		my @redirs = redirs_pull();
		my $redir  = redir_pull_rule();

		@redirs = redir_add( $redir, \@redirs );
		redirs_push( \@redirs );

		return(1);
	}

	sub cb_button_redirremove_clicked
	{
		my @redirs = redirs_pull();
		my $redir  = redir_get_selection();

		if ( $redir )
		{
			@redirs = redir_del( $redir, \@redirs );
			redirs_push( \@redirs );
		}

		return(1);
	}

	sub cb_button_redirapply_clicked
	{
		my $redir     = redir_pull_rule();
		my $old_redir = redir_get_selection();

		my @redirs = ();

		if ( $old_redir )
		{
			@redirs = redir_change( $redir, $old_redir );
			redirs_push( \@redirs );
		}

		return(1);
	}

	sub cb_treeview_redirs_slection_changed
	{
		my $treeview = $glade_vmconfig->get_widget('treeview_redirs');
		my $iter     = $treeview->get_selection()->get_selected();

		my $proto = undef;
		my $hport = undef;
		my $gip   = undef;
		my $gport = undef;

		if ( $iter )
		{
			$proto = $treeview->get_model()->get( $iter, 0 );
			$hport = $treeview->get_model()->get( $iter, 1 );
			$gip   = $treeview->get_model()->get( $iter, 2 );
			$gport = $treeview->get_model()->get( $iter, 3 );
	
			redir_push_rule( $proto.':'.$hport.':'.$gip.':'.$gport );
		}

		return(1);
	}

	return(1);
}

sub vmconfig_window_widgets
{
	foreach my $item (
		gettext( 'Floppy A' ),
		gettext( 'Hard disk 0' ),
		gettext( 'CD-ROM' )
	)
	{
		$glade_vmconfig->get_widget('combobox_boot')->append_text( $item );
	}

	$glade_vmconfig->get_widget('combobox_systype')->
		append_text( $text_by_arch{$arch_by_emu{'qemu'}} );

	foreach my $item ( @apps_extra )
	{
		if ( $apps{$item} and ( $item ne 'kvm') )
		{
			$glade_vmconfig->get_widget('combobox_systype')->
				append_text( $text_by_arch{$arch_by_emu{$item}} );
		}
	}

	$glade_vmconfig->get_widget('combobox_serialdev')->
		append_text( gettext('Default') );
	$glade_vmconfig->get_widget('combobox_monitordev')->
		append_text( gettext('Default') );

	foreach my $item ( 'vc', 'pty', 'null', 'stdio' )
	{
		$glade_vmconfig->get_widget('combobox_serialdev')->append_text( $item );
		$glade_vmconfig->get_widget('combobox_monitordev')->append_text( $item );
	}

	$glade_vmconfig->get_widget('combobox_keyboard')->
		append_text( gettext('Default') );

	foreach my $item ( sort (keys %kbd_val_by_txt) )
	{
		$glade_vmconfig->get_widget('combobox_keyboard')->append_text( $item );
	}

	$glade_vmconfig->get_widget('combobox_keyboard')->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' )
		)
		{
			$glade_vmconfig->get_widget('combobox_nettype'.$idx)->
				append_text( $item );
		}
	}

	my $column0 = Gtk2::TreeViewColumn->
		new_with_attributes(
			gettext("Proto."),
			Gtk2::CellRendererText->new(),
			"text",
			0
	);

	my $column1 = Gtk2::TreeViewColumn->
		new_with_attributes(
			gettext("Host port"),
			Gtk2::CellRendererText->new(),
			"text",
			1
	);

	my $column2 = Gtk2::TreeViewColumn->
		new_with_attributes(
			gettext("Guest IP"),
			Gtk2::CellRendererText->new(),
			"text",
			2
	);

	my $column3 = Gtk2::TreeViewColumn->
		new_with_attributes(
			gettext("Guest port"),
			Gtk2::CellRendererText->new(),
			"text",
			3
	);

	my $liststore = Gtk2::ListStore->new(
		'Glib::String',
		'Glib::String',
		'Glib::String',
		'Glib::String',
		'Glib::String'
	);

	$treeview_redirs = $glade_vmconfig->get_widget('treeview_redirs');
	$treeview_redirs->set_model( $liststore );
	$treeview_redirs->append_column( $column0 );
	$treeview_redirs->append_column( $column1 );
	$treeview_redirs->append_column( $column2 );
	$treeview_redirs->append_column( $column3 );

	if ( ! $apps{'kvm'} )
	{
		$glade_vmconfig->get_widget('radiobutton_accelkvm')->set_sensitive(0);
	}

	return(1);
}

# Get a list of all VM configuration files.
# Args: none.
# Return: sorted list of names (array).
sub vmconfig_list
{
	my $file = undef;
	my @list = ();

	if ( ! -d $vmconf_dir )
	{
		return(0);
	}

	opendir( CFGDIR, $vmconf_dir ) or return(0);

	while ( $file = readdir(CFGDIR) )
	{
		next if ( $file =~ /^\./ );
		push( @list, $file );
	}

	closedir( CFGDIR );

	return( @list );
}

# Save a VM configuration to a file.
# Args: a configuration file to save (string).
# Returns: 1/0.
sub vmconfig_save
{
	my @values = ();

	my $file = $_[0];
	my %config = %{$vmconfig_cache{$file}};

	my $filename = $vmconf_dir.'/'.$file;
	my $fh = new IO::File( $filename, 'w' );

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

	foreach my $var ( sort(keys %config) )
	{
		if ( ( ! defined($config{$var}) ) or ( $config{$var} eq '' ) )
		{
			print( $fh $var.'='.'undef'."\n" );
		}
		else
		{
			print( $fh $var.'='.$config{$var}."\n" );
		}
	}

	close( $fh );

	return(1);
}

# Load a VM configuration from file.
# Args: file name (string).
# Returns: configuration (hash) or 0.
sub vmconfig_load
{
	my %config = %vmconfig_default;

	my $file = $_[0];

	my $filename = $vmconf_dir.'/'.$file;
	my $fh = new IO::File( $filename, 'r' );

	if ( ! $fh )
	{
		dialog_alert(
			sprintf( gettext("Could not load '%s'"), $filename ),
			gettext( 'Make sure you have appropriate permissions' ),
			'error',
			$window_main
		);

		return(0);
	}

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

		if ( /\s*([^:]+)\=(.*)$/ )
		{
			if ( $2 eq 'undef' )
			{
				$config{$1} = undef;
			}
			else
			{
				$config{$1} = $2;
			}
		}
	}

	close( $fh );

	return( \%config );
}

# Read all VM configurations into a hash.
# Args: none.
# Returns: 1, writes to a global variable.
sub vmconfig_cache
{
	my $item = undef;

	my @vmlist = vmconfig_list();

	if ( @vmlist )
	{
		foreach $item ( @vmlist )
		{
			$vmconfig_cache{$item} = ( vmconfig_load($item) );
		}
	}

	return(1);
}

# Populate a VM configuration list.
# Args: none.
# Returns: 1.
sub vmconfig_populate
{
	my $model = $glade_main->get_widget('treeview_main')->get_model();
	my $iter  = $model->get_iter_first();

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

	my $name    = undef;
	my $comment = undef;
	my $item    = undef;

	if ( vmconfig_notempty() )
	{
		foreach $item ( sort({ $b cmp $a }(keys %vmconfig_cache)) )
		{
			my @hardware = ();
			my @features = ();

			$name =    $vmconfig_cache{$item}{'name'};
			$comment = $vmconfig_cache{$item}{'comment'};

			if ( $vmconfig_cache{$item}{'systype'} )
			{
				push( @hardware, $vmconfig_cache{$item}{'systype'} );
			}

			if ( $vmconfig_cache{$item}{'ram'} )
			{
				push( @hardware, $vmconfig_cache{$item}{'ram'}.' MB' );
			}

			if ( $vmconfig_cache{$item}{'numnics'} > 0 )
			{
				push( @features, gettext('network') );
			}
			else
			{
				push( @features, gettext('no network') );
			}

			if ( $vmconfig_cache{$item}{'audio'} > 0 )
			{
				push( @features, gettext('audio') );
			}
			else
			{
				push( @features, gettext('no audio') );
			}

			if (
				( $vmconfig_cache{$item}{'icon'} eq 'default' ) or
				(
					( ! -e $vmconfig_cache{$item}{'icon'} ) and
					( ! -s $vmconfig_cache{$item}{'icon'} ) and
					( ! -r $vmconfig_cache{$item}{'icon'} )
				)
			)
			{
				$model->set(
					$model->append(),
					0, $icon_default,
					1, "<b>".$name."</b>"."\n".
						join(", ", @hardware)."\n".
						join(", ", @features),
					2, $item );
			}
			else
			{
				$model->set(
					$model->append(),
					0, Gtk2::Gdk::Pixbuf->
						new_from_file( $vmconfig_cache{$item}{'icon'} ),
					1, "<b>".$name."</b>"."\n".
						join(", ", @hardware)."\n".
						join(", ", @features),
					2, $item );
			}
		}
	}

	return(1);
}

sub vmconfig_notempty
{
	if ( %vmconfig_cache )
	{
		misc_activate_gui(1);
		return(1);
	}
	else
	{
		misc_activate_gui(0);
		return(0);
	}
}

# Set the GUI values to what is set in the given configuration hash.
# Args: a VM configuration (hashref).
# Returns: 1.
sub vmconfig_push
{
	my $config = $_[0];
	my $item   = undef;

	# Entries #
	foreach $item ( keys %vmconfig_entries )
	{
		if ( $config->{$item} )
		{
			$glade_vmconfig->get_widget('entry_'.$item)->
				set_text( $config->{$item} );
		}
		else
		{
			$glade_vmconfig->get_widget('entry_'.$item)->set_text( '' );
		}
	}

	# File chooser buttons #
	foreach $item ( keys %vmconfig_choosers )
	{
		# This is a major performance hit. Not good...
		$glade_vmconfig->get_widget('chooser_'.$item)->
			set_current_folder( $prefer{'datadir'} );

		if ( $config->{$item} )
		{
			$glade_vmconfig->get_widget('chooser_'.$item)->
				set_filename( $config->{$item} );
		}
		else
		{
			$glade_vmconfig->get_widget('chooser_'.$item)->unselect_all();
		}
	}

	# Diectory chooser buttons #
	foreach $item ( keys %vmconfig_dirchoosers )
	{
		if ( $config->{$item} )
		{
			$glade_vmconfig->get_widget('dirchooser_'.$item)->
				set_filename( $config->{$item} );
		}
	}

	# Check buttons #
	foreach $item ( keys %vmconfig_checkbuttons )
	{
		$glade_vmconfig->get_widget('checkbutton_'.$item)->
			set_active( $config->{$item} );
	}

	# Toggle buttons #
	foreach $item ( keys %vmconfig_togglebuttons )
	{
		$glade_vmconfig->get_widget('togglebutton_'.$item)->
			set_active( $config->{$item} );
	}

	# Spin buttons #
	foreach $item ( keys %vmconfig_spinbuttons )
	{
		$glade_vmconfig->get_widget('spinbutton_'.$item)->
			set_value( $config->{$item} );
	}

	# Combo boxes #
	$glade_vmconfig->get_widget('combobox_boot')->
		set_active( $boot_val_by_txt{ $config->{'boot'} } );
	$glade_vmconfig->get_widget('combobox_systype')->
		set_active( $sys_val_by_txt{ $config->{'systype'} } );

	for ( $idx = 0; $idx <= 7; $idx++ )
	{
		$glade_vmconfig->get_widget('combobox_nettype'.$idx)->
			set_active( $net_val_by_txt{ $config->{'nettype'.$idx} } );
	}

	if ( $config->{'serialdev'} )
	{
		$glade_vmconfig->get_widget('combobox_serialdev')->
			set_active( $dev_val_by_txt{ $config->{'serialdev'} } );
	}
	else
	{
		$glade_vmconfig->get_widget('combobox_serialdev')->set_active(0);
	}

	if ( $config->{'keyboard'} )
	{
		$glade_vmconfig->get_widget('combobox_keyboard')->
			set_active( $kbd_val_by_txt{ $config->{'keyboard'} } );
	}
	else
	{
		$glade_vmconfig->get_widget('combobox_keyboard')->set_active(0);
	}

	if ( $config->{'monitordev'} )
	{
		$glade_vmconfig->get_widget('combobox_monitordev')->
			set_active( $dev_val_by_txt{ $config->{'monitordev'} } );
	}
	else
	{
		$glade_vmconfig->get_widget('combobox_monitordev')->set_active(0);
	}

	# Radio buttons #
	if ( $config->{'gfxtype'} eq 'pcivga' )
	{
		$glade_vmconfig->get_widget('radiobutton_gfxpci')->set_active(1);
	}
	elsif ( $config->{'gfxtype'} eq 'vga' )
	{
		$glade_vmconfig->get_widget('radiobutton_gfxvga')->set_active(1);
	}

	if ( $config->{'sndtype'} eq 'sb16' )
	{
		$glade_vmconfig->get_widget('radiobutton_sndsb')->set_active(1);
	}
	elsif ( $config->{'sndtype'} eq 'es1370' )
	{
		$glade_vmconfig->get_widget('radiobutton_sndes')->set_active(1);
	}

	if ( $config->{'acceltype'} eq 'disable' )
	{
		$glade_vmconfig->get_widget('radiobutton_acceloff')->set_active(1);
	}
	elsif ( $config->{'acceltype'} eq 'enable' )
	{
		$glade_vmconfig->get_widget('radiobutton_accelon')->set_active(1);
	}
	elsif ( $config->{'acceltype'} eq 'full' )
	{
		$glade_vmconfig->get_widget('radiobutton_accelfull')->set_active(1);
	}
	elsif ( $config->{'acceltype'} eq 'kvm' )
	{
		$glade_vmconfig->get_widget('radiobutton_accelkvm')->set_active(1);
	}

	# Non-standard/custom widgets #
	my @redirs = ();
	if ( $config->{'portredirs'} )
	{
		@redirs = split( ';', $config->{'portredirs'} );
		redirs_push( \@redirs );
	}
	else
	{
		redirs_clear();
	}

	return(1);
}

sub vmconfig_pull
{
	my %config = %vmconfig_default;
	my $item   = undef;

	$config{'name'} = $vmconfig_cache{vmconfig_getselection()}{'name'};
	$config{'icon'} = $vmconfig_cache{vmconfig_getselection()}{'icon'};

	# Entries #
	foreach $item ( keys %vmconfig_entries )
	{
		$config{$item} =
			$glade_vmconfig->get_widget('entry_'.$item)->get_text();
	}

	# File chooser buttons #
	foreach $item ( keys %vmconfig_choosers )
	{
		$config{$item} =
			$glade_vmconfig->get_widget('chooser_'.$item)->get_filename();
	}

	# Diectory chooser buttons #
	foreach $item ( keys %vmconfig_dirchoosers )
	{
		$config{$item} =
			$glade_vmconfig->get_widget('dirchooser_'.$item)->get_filename();
	}

	# Check buttons #
	foreach $item ( keys %vmconfig_checkbuttons )
	{
		# Why does it not return int?
		my $state = 
			$glade_vmconfig->get_widget('checkbutton_'.$item)->get_active();

		if ( $state )
		{
			$config{$item} = 1;
		}
		else
		{
			$config{$item} = 0;
		}
	}

	# Toggle buttons #
	foreach $item ( keys %vmconfig_togglebuttons )
	{
		# Why does it not return int?
		my $state = 
			$glade_vmconfig->get_widget('togglebutton_'.$item)->get_active();

		if ( $state )
		{
			$config{$item} = 1;
		}
		else
		{
			$config{$item} = 0;
		}
	}

	# Spin buttons #
	foreach $item ( keys %vmconfig_spinbuttons )
	{
		$config{$item} =
			$glade_vmconfig->get_widget('spinbutton_'.$item)->get_value_as_int();
	}

	# Combo boxes #
	$config{'boot'} =
		$boot_val_by_num{$glade_vmconfig->
			get_widget('combobox_boot')->get_active()};
	$config{'systype'} =
		$sys_val_by_num{$glade_vmconfig->
			get_widget('combobox_systype')->get_active()};

	for ( $idx = 0; $idx <= 7; $idx++ )
	{
		$config{'nettype'.$idx} =
			$net_val_by_num{$glade_vmconfig->
				get_widget('combobox_nettype'.$idx)->get_active()};
	}

	$config{'serialdev'} =
		$dev_val_by_num{$glade_vmconfig->
			get_widget('combobox_serialdev')->get_active()};
	$config{'keyboard'} =
		$kbd_val_by_num{$glade_vmconfig->
			get_widget('combobox_keyboard')->get_active()};
	$config{'monitordev'} =
		$dev_val_by_num{$glade_vmconfig->
			get_widget('combobox_monitordev')->get_active()};

	# Radio buttons #
	if ( $glade_vmconfig->get_widget('radiobutton_gfxpci')->get_active() )
	{
		$config{'gfxtype'} = 'pcivga';
	}
	elsif ( $glade_vmconfig->get_widget('radiobutton_gfxvga')->get_active() )
	{
		$config{'gfxtype'} = 'vga';
	}

	if ( $glade_vmconfig->get_widget('radiobutton_sndsb')->get_active() )
	{
		$config{'sndtype'} = 'sb16';
	}
	elsif ( $glade_vmconfig->get_widget('radiobutton_sndes')->get_active() )
	{
		$config{'sndtype'} = 'es1370';
	}

	if ( $glade_vmconfig->get_widget('radiobutton_acceloff')->get_active() )
	{
		$config{'acceltype'} = 'disable';
	}
	elsif ( $glade_vmconfig->get_widget('radiobutton_accelon')->get_active() )
	{
		$config{'acceltype'} = 'enable';
	}
	elsif ( $glade_vmconfig->get_widget('radiobutton_accelfull')->get_active() )
	{
		$config{'acceltype'} = 'full';
	}
	elsif ( $glade_vmconfig->get_widget('radiobutton_accelkvm')->get_active() )
	{
		$config{'acceltype'} = 'kvm';
	}

	# Non-standard/custom widgets #
	$config{'portredirs'} = join( ';', redirs_pull() );

	$vmconfig_cache{vmconfig_getselection()} = \%config;

	return(1);
}

# Create a new VM configuration.
# Args: none.
# Returns: 1/0.
sub vmconfig_new
{
	my $name = dialog_vmname();
	my $item = misc_timestamp();

	my %config = %vmconfig_default;

	if ( $name and $item )
	{
		$vmconfig_cache{$item} = \%config;
		$vmconfig_cache{$item}{'name'} = $name;

		vmconfig_save( $item );
		vmconfig_populate();
		vmconfig_select( $item );
		vmconfig_window_show( $name );

		return(1);
	}
	else
	{
		return(0);
	}
}

# Edit selected VM configuration.
# Args: none, reads global variable.
# Return: 1.
sub vmconfig_edit
{
	my $button = $glade_main->get_widget('toggletoolbutton_edit');

	if ( $vmconfig_current )
	{
		if ( $button->get_active() ) #### !!!! ####
		{
			vmconfig_window_show( $vmconfig_current->{'name'} );
			vmconfig_push( $vmconfig_current );
		}
		else
		{
			$window_vmconfig->hide();
		}
	}
	else
	{
		$button->set_active(0);
	}

	return(1);
}

# Select a given configuration.
# Args: configuration to select (string).
# Returns: 1.
sub vmconfig_select
{
	if ( %vmconfig_cache )
	{
		my $item = $_[0];
		my $iter = $treeview_main->get_model()->get_iter_first();

		while ( $iter )
		{
			if ( $treeview_main->get_model()->get($iter, 2) eq $item )
			{
				$treeview_main->get_selection()->select_iter( $iter );
				return(1);
			}
			else
			{
				$iter = $treeview_main->get_model()->iter_next( $iter );
			}
		}
	}

	return(1);
}

# Select first VM configuration.
# Args: none.
# Returns: 1.
sub vmconfig_selectfirst
{
	if ( %vmconfig_cache )
	{
		my $iter = $treeview_main->get_model()->get_iter_first();
		$treeview_main->get_selection()->select_iter( $iter );
	}

	return(1);
}

# Delete current VM configuration.
# Args: none, reads global variable.
# Returns: 1/0.
sub vmconfig_delete
{
	my $file = vmconfig_getselection();

	if ( %vmconfig_cache and $vmconfig_current and $file )
	{
		my $name = $vmconfig_current->{'name'};

		if (
			dialog_question(
				sprintf( gettext("Delete '%s'?"), $name ),
				gettext( 'The configuration will be lost' ),
				'gtk-delete',
				$window_main
			)
		)
		{
			#$window_vmconfig->hide();

			if ( unlink($vmconf_dir.'/'.$file) )
			{
				delete( $vmconfig_cache{vmconfig_getselection()} );
				vmconfig_populate();
				vmconfig_selectfirst();
				return(1);
			}
			else
			{
				dialog_alert(
					sprintf( gettext("Could not delete '%s'"), $name ),
					gettext( 'Make sure you have apropriate permissions' ),
					'error',
					$window_main
				);
			}
		}
	}

	return(0);
}

# Get selected VM configuration.
# Args: none.
# Returns: selected VM configuration (string).
sub vmconfig_getselection
{
	my $iter = $treeview_main->get_selection()->get_selected();

	if ( %vmconfig_cache and $iter )
	{
		my $item = $treeview_main->get_model()->get( $iter, 2 );
		$vmconfig_current = $vmconfig_cache{$item};

		return( $item );
	}
	else
	{
		$vmconfig_current = undef;

		return(0);
	}
}

#
#
#
sub vmconfig_launch
{
	my $item = $_[0];
	my $qcmd = undef;

	my $config = $vmconfig_cache{$item};

	my @qcmd_parts = ();

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

	if (
		( $config->{'acceltype'} eq 'kvm' ) and
		( $config->{'systype'} eq 'x86_64' )
	)
	{
		push( @qcmd_parts, $apps{'kvm'} );
	}
	elsif ( $config->{'systype'} )
	{
		push( @qcmd_parts, $apps{$emu_by_arch{$config->{'systype'}}} );
	}

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

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

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

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

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

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

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

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

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

	if ( $config->{'usecdrom'} and $config->{'cdrom'} )
	{
		push( @qcmd_parts, '-cdrom '."'".$config->{'cdrom'}."'" );
	}
	elsif ( $config->{'hdc'} )
	{
		push( @qcmd_parts, '-hdc '.$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->{'netmac'.$idx} )
			{
				push(
					@qcmd_parts,
					'-net nic,vlan='.$config->{'netvlan'.$idx}.
					',macaddr='.$config->{'netmac'.$idx}
				);
			}
			else
			{
				push(
					@qcmd_parts,
					'-net nic,vlan='.$config->{'netvlan'.$idx}
				);
			}

			if ( $config->{'nettype'.$idx} eq 'usermode' )
			{
				push(
					@qcmd_parts,
					'-net user,vlan='.$config->{'netvlan'.$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->{'tapscript'.$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->{'netport'.$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->{'netvlan'.$idx}.
					',fd='.$config->{'netfd'.$idx}
				);
			}
			elsif ( $config->{'nettype'.$idx} eq 'multicast' )
			{
				if ( $config->{'netip'.$idx} )
				{
					push(
						@qcmd_parts,
						'-net socket,vlan='.$config->{'netvlan'.$idx}.
						',mcast='.$config->{'netip'.$idx}.
						':'.$config->{'netport'.$idx}
					);
				}
				else
				{
					push(
						@qcmd_parts,
						'-net socket,vlan='.$config->{'netvlan'.$idx}.
						',mcast=:'.$config->{'netport'.$idx}
					);
				}
			}
			elsif ( $config->{'nettype'.$idx} eq 'multicastfd' )
			{
				push(
					@qcmd_parts,
					'-net socket,vlan='.$config->{'netvlan'.$idx}.
					',fd='.$config->{'netfd'.$idx}
				);
			}
		}

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

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

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

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

	if ( $config->{'audio'} )
	{
		push( @qcmd_parts, '-soundhw '.$config->{'sndtype'} );
	}

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

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

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

	if ( $config->{'nogfx'} )
	{
		push( @qcmd_parts, '-nographic' );
	}
	elsif ( $config->{'gfxtype'} eq 'vga' )
	{
		push( @qcmd_parts, '-std-vga' );
	}

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

	my @logitems = ();

	foreach my $item (
		'cpu',
		'exec',
		'in_asm',
		'int',
		'op_opt',
		'op',
		'out_asm',
		'pcall'
	)
	{
		if ( $config->{'log_'.$item} )
		{
			push( @logitems, $item );
		}
	}

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

	if ( $config->{'acceltype'} eq 'disable' )
	{
		push( @qcmd_parts, '-no-kqemu' );
	}
	elsif ( $config->{'acceltype'} eq 'full' )
	{
		push( @qcmd_parts, '-kernel-kqemu' );
	}

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

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

	$qcmd = join( ' ', @qcmd_parts );

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

	print( $qcmd."\n" );
	system( $qcmd );
}

1;
