The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Padre::Wx::Dialog::WindowList;

use 5.008;
use strict;
use warnings;
use Padre::Wx       ();
use Padre::Wx::Icon ();

our $VERSION = '0.96';
our @ISA     = 'Wx::Dialog';

use Class::XSAccessor {
	accessors => {
		_butdelete   => '_butdelete',   # delete button
		_butopen     => '_butopen',     # open button
		_list        => '_list',        # list on the left of the pane
		_sortcolumn  => '_sortcolumn',  # column used for list sorting
		_sortreverse => '_sortreverse', # list sorting is reversed
		_vbox        => '_vbox',        # the window vbox sizer
	}
};

# -- constructor

sub new {
	my $class  = shift;
	my $parent = shift;
	my %args   = @_;

	# create object
	my $self = $class->SUPER::new(
		$parent,
		-1,
		Wx::gettext( $args{title} || Wx::gettext('Window list') ),
		Wx::DefaultPosition,
		Wx::Size->new( 480, 300 ),
		Wx::DEFAULT_FRAME_STYLE | Wx::TAB_TRAVERSAL,
	);

	foreach ( keys %args ) {
		$self->{$_} = $args{$_};
	}

	$self->{button_clicks} = [
		\&_button_clicked_0, \&_button_clicked_1,
		\&_button_clicked_2, \&_button_clicked_3, \&_button_clicked_4,
		\&_button_clicked_5, \&_button_clicked_6, \&_button_clicked_7,
		\&_button_clicked_8, \&_button_clicked_9
	];

	$self->SetIcon(Padre::Wx::Icon::PADRE);

	if ( scalar Padre->ide->wx->main->editors ) {

		# Create dialog
		$self->_create;
	} else {
		$self->{_empty} = 1;
	}

	return $self;
}

# -- public methods

sub show {
	my $self = shift;

	if ( $self->{_empty} ) {
		$self->Destroy;
		return 0;
	}

	$self->{visible} = 1;

	$self->_refresh_list;
	$self->_select_first_item;
	$self->ShowModal;
}

# -- gui handlers

#
# $self->_on_butclose_clicked;
#
# handler called when the close button has been clicked.
#
sub _on_butclose_clicked {
	my $self = shift;
	$self->Destroy;
	$self->{visible} = 0;
}

sub _on_button_clicked {
	my $self      = shift;
	my $button_no = shift;

	my @pages;

	foreach my $listitem ( 0 .. ( $self->_list->GetItemCount - 1 ) ) {
		my $item = $self->{items}->[ $self->_list->GetItem($listitem)->GetData ];
		next unless $item->{selected};
		push @pages, $item->{page};
	}

	my $code = $self->{buttons}->[$button_no]->[1];

	if ( ref($code) eq 'CODE' ) {
		&{$code}(@pages);
	} else {
		warn 'Button code is no CODE reference: ' . $code . ' (' . ref($code) . ')';
	}

	$self->Destroy;
	$self->{visible} = 0;
}

#
# $self->_on_list_col_click;
#
# handler called when a column has been clicked, to reorder the list.
#
sub _on_list_col_click {
	my ( $self, $event ) = @_;
	my $col = $event->GetColumn;

	my $prevcol  = $self->_sortcolumn  || 0;
	my $reversed = $self->_sortreverse || 0;
	$reversed = $col == $prevcol ? !$reversed : 0;
	$self->_sortcolumn($col);
	$self->_sortreverse($reversed);
	$self->_refresh_list( $col, $reversed );
}

#
# $self->_on_list_item_selected( $event );
#
# handler called when a list item has been selected. it will in turn update
# the buttons state.
#
# $event is a Wx::ListEvent.
#
sub _on_list_item_selected {
	my ( $self, $event ) = @_;

	$self->{items}->[ $event->GetIndex ]->{selected} = 1;

	# update buttons
	$self->_update_buttons_state;
}

#
# $self->_on_list_item_deselected( $event );
#
# handler called when a list item has lost selection. it will in turn update
# the buttons state.
#
# $event is a Wx::ListEvent.
#
sub _on_list_item_deselected {
	my ( $self, $event ) = @_;

	$self->{items}->[ $event->GetIndex ]->{selected} = 0;

	# update buttons
	$self->_update_buttons_state;
}

# -- private methods

#
# $self->_create;
#
# create the dialog itself. it will have a list with all open files, and
# some buttons to manage them.
#
# no params, no return values.
#
sub _create {
	my $self = shift;

	# create vertical box that will host all controls
	my $vbox = Wx::BoxSizer->new(Wx::VERTICAL);
	$self->SetSizer($vbox);
	$self->CenterOnParent;

	#$self->SetMinSize( [ 640, 480 ] );
	$self->_vbox($vbox);

	$self->_create_list;
	$self->_create_options;
	$self->_create_buttons;
	$self->_list->SetFocus;
}

#
# $dialog->_create_list;
#
# create the open file list.
#
# no params. no return values.
#
sub _create_list {
	my $self = shift;
	my $vbox = $self->_vbox;

	# title label
	my $label = Wx::StaticText->new(
		$self, -1,
		$self->{list_title} || Wx::gettext('List of open files')
	);
	$vbox->Add( $label, 0, Wx::ALL, 5 );

	# create list
	my $list = Wx::ListCtrl->new(
		$self,
		-1,
		Wx::DefaultPosition,
		Wx::DefaultSize,
		Wx::LC_REPORT,
	);
	$list->InsertColumn( 0, Wx::gettext('Project') );
	$list->InsertColumn( 1, Wx::gettext('File') );
	$list->InsertColumn( 2, Wx::gettext('Editor') );
	$list->InsertColumn( 3, Wx::gettext('Disk') );
	$self->_list($list);

	# install event handler
	Wx::Event::EVT_LIST_ITEM_DESELECTED( $self, $list, \&_on_list_item_deselected );
	Wx::Event::EVT_LIST_ITEM_SELECTED( $self, $list, \&_on_list_item_selected );
	Wx::Event::EVT_LIST_COL_CLICK( $self, $list, \&_on_list_col_click );

	# pack the list
	$vbox->Add( $list, 1, Wx::ALL | Wx::EXPAND, 5 );
}

#
# $dialog->_create_options;
#
# create the options
#
# no params. no return values.
#
sub _create_options {
	my $self = shift;

	my $config = Padre->ide->config;

	# the hbox
	my $hbox = Wx::BoxSizer->new(Wx::HORIZONTAL);
	$self->_vbox->Add( $hbox, 0, Wx::ALL | Wx::EXPAND, 5 );

	# CheckBox
	#	$self->{autosave} = Wx::CheckBox->new(
	#		$self,
	#		-1,
	#		Wx::gettext('Save session automatically'),
	#	);
	#	$self->{autosave}->SetValue( $config->session_autosave ? 1 : 0 );
	#
	#	$hbox->Add( $self->{autosave}, 0, Wx::ALL, 5 );
}

#
# $dialog->_create_buttons;
#
# create the buttons pane.
#
# no params. no return values.
#
sub _create_buttons {
	my $self = shift;

	# the hbox
	my $hbox = Wx::BoxSizer->new(Wx::HORIZONTAL);
	$self->_vbox->Add( $hbox, 0, Wx::ALL | Wx::EXPAND, 5 );

	# the buttons
	my $bc = Wx::Button->new( $self, Wx::ID_CANCEL, Wx::gettext('Close') );
	Wx::Event::EVT_BUTTON( $self, $bc, \&_on_butclose_clicked );

	foreach my $button_no ( 0 .. $#{ $self->{buttons} || [] } ) {
		if ( !defined( $self->{button_clicks}->[$button_no] ) ) {
			warn 'Too many buttons defined!';
			last;
		}
		my $button = $self->{buttons}->[$button_no];
		$button->[2] = Wx::Button->new( $self, -1, $button->[0] );
		Wx::Event::EVT_BUTTON( $self, $button->[2], $self->{button_clicks}->[$button_no] );
		$hbox->Add( $button->[2], 0, Wx::ALL, 5 );
	}
	$hbox->AddStretchSpacer;
	$hbox->Add( $bc, 0, Wx::ALL, 5 );
}

#
# $dialog->_refresh_list($column, $reverse);
#
# refresh list of open files. list is sorted according to $column (default to
# first column), and may be reversed (default to no).
#
sub _refresh_list {
	my ( $self, $column, $reverse ) = @_;

	my $main = Padre->ide->wx->main;

	# default sorting
	$column  ||= 0;
	$reverse ||= 0;

	# clear list & fill it again
	my $list = $self->_list;
	$list->DeleteAllItems;
	$self->{items} = []; # Clear
	foreach my $editor ( $main->editors ) {

		my $document = $editor->{Document};

		my $disk_state = $document->has_changed_on_disk;
		next if $self->{no_fresh} and ( !( $document->is_modified or $disk_state ) );

		my $filename;

		my $documentfile = $document->file;
		if ( defined($documentfile) ) {

			$filename = $documentfile->filename;
			my $project_dir = $document->project_dir;
			$filename =~ s/^\Q$project_dir\E// if defined($project_dir);

			# Apply filter (if any)
			if ( defined( $self->{filter} ) ) {
				next unless &{ $self->{filter} }( $editor, $project_dir, $filename, $document );
			}
		} else {
			$filename = $document->get_title;
		}

		# inserting the file in the list
		my $item = Wx::ListItem->new;
		$item->SetId(0);
		$item->SetColumn(0);
		$item->SetText( defined( $document->project ) ? $document->project->name : '' );
		$item->SetData( $#{ $self->{items} } );
		my $idx = $list->InsertItem($item);
		splice @{ $self->{items} }, $idx, 0, { page => $editor };

		$list->SetItem( $idx, 1, $filename );
		$list->SetItem( $idx, 2, $document->is_modified ? Wx::gettext('CHANGED') : Wx::gettext('fresh') );

		my $disk_text;
		if ( $disk_state == 0 ) {
			$disk_text = Wx::gettext('fresh');
		} elsif ( $disk_state == -1 ) {
			$disk_text = Wx::gettext('DELETED');
		} else {
			$disk_text = Wx::gettext('CHANGED');
		}
		$list->SetItem( $idx, 3, $disk_text );
	}

	# auto-resize columns
	my $flag =
		$list->GetItemCount
		? Wx::LIST_AUTOSIZE
		: Wx::LIST_AUTOSIZE_USEHEADER;
	$list->SetColumnWidth( $_, $flag ) for 0 .. 2;

	# making sure the list can show all columns
	my $width = 15; # taking vertical scrollbar into account
	$width += $list->GetColumnWidth($_) for 0 .. 2;
	$list->SetMinSize( [ $width, -1 ] );
}

#
# $self->_select_first_item;
#
# select first item in the list, or none if there are none. in that case,
# update the current row and name selection to undef.
#
sub _select_first_item {
	my $self = shift;

	# Select first item in the list
	my $list = $self->_list;

	if ( $list->GetItemCount ) {
		my $item = $list->GetItem(0);
		$item->SetState(Wx::LIST_STATE_SELECTED);
		$list->SetItem($item);
	} else {

		# Remove current selection
		foreach my $method (qw{ _currow _curname }) {
			next unless $self->can($method);
			$self->$method(undef);
		}
	}
}

#
# $self->_update_buttons_state;
#
# update state of delete and open buttons: they should not be clickable if nothing
# is selected.
#
sub _update_buttons_state {
	my ($self) = @_;

	my $count;
	foreach my $item ( @{ $self->{items} } ) {
		++$count if $item->{selected};
	}

	my $method = $count ? 'Enable' : 'Disable';

	foreach my $button ( @{ $self->{buttons} || [] } ) {
		$button->[2]->$method if defined( $button->[2] );
	}

}

# Sorry, I found no other way to solve this
sub _button_clicked_0 { $_[0]->_on_button_clicked(0); }
sub _button_clicked_1 { $_[0]->_on_button_clicked(1); }
sub _button_clicked_2 { $_[0]->_on_button_clicked(2); }
sub _button_clicked_3 { $_[0]->_on_button_clicked(3); }
sub _button_clicked_4 { $_[0]->_on_button_clicked(4); }
sub _button_clicked_5 { $_[0]->_on_button_clicked(5); }
sub _button_clicked_6 { $_[0]->_on_button_clicked(6); }
sub _button_clicked_7 { $_[0]->_on_button_clicked(7); }
sub _button_clicked_8 { $_[0]->_on_button_clicked(8); }
sub _button_clicked_9 { $_[0]->_on_button_clicked(9); }

1;

__END__


=head1 NAME

Padre::Wx::Dialog::WindowList - Windows list dialog for Padre



=head1 DESCRIPTION

This module could be used to apply custom actions to some of the open files/windows.


=head1 PUBLIC API

=head2 Constructor

=head3 C<new>

    my $dialog = PWD::SM->new( $parent )

Create and return a new Wx dialog listing all the windows. It needs a
C<$parent> window (usually Padre's main window).

=head2 Public methods

=head3 C<show>

    $dialog->show;

Request the window list dialog to be shown. It will be refreshed first with
a current list of open files/windows.


=head1 COPYRIGHT & LICENSE

Copyright 2008-2012 The Padre development team as listed in Padre.pm.

This program is free software; you can redistribute
it and/or modify it under the same terms as Perl 5 itself.


=cut