The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

use List::Util qw(min max) ;

#----------------------------------------------------------------------------------------------

register_action_handlers
	(
	'Align objects'=> 
		{
		SHORTCUTS => '0A0-a',
		
		'Align left'   => ['000-l', \&align, 'left'],
		'Align center' => ['000-c', \&align, 'center'],
		'Align right'  => ['000-r', \&align, 'right'],
		'Align top'    => ['000-t', \&align, 'top'],
		'Align middle' => ['000-m', \&align, 'middle'],
		'Align bottom' => ['000-b', \&align, 'bottom'],
		# spread vertically	# spread horizontally # adjacent vert # adjacent hor # stack
		},
	) ;

#----------------------------------------------------------------------------------------------

sub align
{
my ($self, $alignment) = @_ ;

$self->create_undo_snapshot() ;

my @elements_to_move =  grep {my @connectors = $_->get_connector_points() ; @connectors == 0 } $self->get_selected_elements(1) ;

for ($alignment)
	{
	$_ eq 'left' and do
		{
		my $left = min( map{$_->{X}} @elements_to_move) ;
			
		for my $element (@elements_to_move)
			{
			$self->move_elements($left - $element->{X},0, $element) ;
			}
		last ;
		} ;
		
	$_ eq 'center' and do
		{
		my $left = min( map{$_->{X}} @elements_to_move) ;
		my $right = max
					(
					map
						{
						my ($w, $h) = $_->get_size() ;
						
						$_->{X} + $w ;
						} @elements_to_move
					) ;
					
		my $center = int(($left + $right) / 2) ;
		
		# find element which center is closes to geometric center
		my $closest_element = undef ;
		my $closest_element_distance =  1_000_000 ;
		my $closest_center ;
		
		for my $element (@elements_to_move)
			{
			my ($w, $h)  = $element->get_size() ;
			
			my $element_center = $element->{X} + int($w / 2) ;
			my $center_to_center_distance = abs($center - $element_center) ;
			
			if($center_to_center_distance <$closest_element_distance)
				{
				$closest_element = $element ;
				$closest_element_distance = $center_to_center_distance;
				$closest_center = $element_center ;
				}
			}
			
		for my $element (@elements_to_move)
			{
			next if $element == $closest_element ;
			
			my ($w, $h)  = $element->get_size() ;
			my $element_center = $element->{X} + int($w / 2) ;
			
			$self->move_elements($closest_center - $element_center, 0, $element) ;
			}
		last ;
		} ;
		
	$_ eq 'right' and do
		{
		my $right = max
					(
					map
						{
						my ($w, $h) = $_->get_size() ;
						
						$_->{X} + $w ;
						} @elements_to_move
					) ;
			
		for my $element (@elements_to_move)
			{
			my ($w, $h)  = $element->get_size() ;
			$self->move_elements($right - ($element->{X} + $w), 0, $element) ;
			}
		last ;
		} ;
		
	$_ eq 'top' and do
		{
		my $top = min( map{$_->{Y}} @elements_to_move) ;
			
		for my$element (@elements_to_move)
			{
			$self->move_elements(0, $top - $element->{Y}, $element) ;
			}
		last ;
		} ;
		
	$_ eq 'middle' and do
		{
		my $top = min( map{$_->{Y}} @elements_to_move) ;
		my $bottom = max
					(
					map
						{
						my ($w, $h) = $_->get_size() ;
						
						$_->{Y} + $h ;
						} @elements_to_move
					) ;
					
		my $center = int(($top + $bottom) / 2) ;
		
		# find element which center is closes to geometric center
		my $closest_element = undef ;
		my $closest_element_distance =  1_000_000 ;
		my $closest_center ;
		
		for my $element (@elements_to_move)
			{
			my ($w, $h)  = $element->get_size() ;
			
			my $element_center = $element->{Y} + int($h / 2) ;
			my $center_to_center_distance = abs($center - $element_center) ;
			
			if($center_to_center_distance <$closest_element_distance)
				{
				$closest_element = $element ;
				$closest_element_distance = $center_to_center_distance;
				$closest_center = $element_center ;
				}
			}
			
		for my $element (@elements_to_move)
			{
			next if $element == $closest_element ;
			
			my ($w, $h)  = $element->get_size() ;
			my $element_center = $element->{Y} + int($h / 2) ;
			
			$self->move_elements(0, $closest_center - $element_center, $element) ;
			}
		last ;
		} ;
		
	$_ eq 'bottom' and do
		{
		my $bottom = max
					(
					map
						{
						my ($w, $h) = $_->get_size() ;
						
						$_->{Y} + $h ;
						} @elements_to_move
					) ;
			
		for my $element (@elements_to_move)
			{
			my ($w, $h)  = $element->get_size() ;
			$self->move_elements(0, $bottom - ($element->{Y} + $h), $element) ;
			}
		last ;
		} ;
	}

$self->update_display() ;
}