/usr/local/CPAN/JaM/JaM/GUI/IO_Filter.pm


# $Id: IO_Filter.pm,v 1.3 2001/09/02 11:15:26 joern Exp $

package JaM::GUI::IO_Filter;

@ISA = qw ( JaM::GUI::Window );

use strict;
use JaM::GUI::Window;
use JaM::Filter::IO;

my $DEBUG = 1;

# get/set gtk object for subjects clist
sub gtk_win		{ my $s = shift; $s->{gtk_win}
		          = shift if @_; $s->{gtk_win}		}

sub gtk_filter_list	{ my $s = shift; $s->{gtk_filter_list}
		          = shift if @_; $s->{gtk_filter_list}	}

sub gtk_filter_frame	{ my $s = shift; $s->{gtk_filter_frame}
		          = shift if @_; $s->{gtk_filter_frame}	}

sub gtk_folder_menu	{ my $s = shift; $s->{gtk_folder_menu}
		          = shift if @_; $s->{gtk_folder_menu}	}

sub gtk_filter_folder	{ my $s = shift; $s->{gtk_filter_folder}
		          = shift if @_; $s->{gtk_filter_folder} }

sub gtk_filter_vbox	{ my $s = shift; $s->{gtk_filter_vbox}
		          = shift if @_; $s->{gtk_filter_vbox}  }

sub filter_ids		{ my $s = shift; $s->{filter_ids}
		          = shift if @_; $s->{filter_ids}	}

sub selected_filter	{ my $s = shift; $s->{selected_filter}
		          = shift if @_; $s->{selected_filter}	}

sub filter_type		{ my $s = shift; $s->{filter_type}
		          = shift if @_; $s->{filter_type}	}

sub single_instance_window { 1 }

sub build {
	my $self = shift; $self->trace_in;

	my $win = Gtk::Window->new;
	$win->set_position ("center");
	$win->set_title ("Edit Input/Output Filter");
	$win->border_width(3);
	$win->set_default_size (530, 500);
	$win->realize;
	$win->show;

	my $vpane = new Gtk::VPaned();
	$vpane->show();
	$win->add ($vpane);
	$vpane->set_handle_size( 10 );
	$vpane->set_gutter_size( 15 );
	
	my $fr = Gtk::Frame->new ("Select filter");
	$fr->show;

	my $hbox = Gtk::HBox->new(0,5);
	$hbox->show;
	$hbox->set_border_width(5);
	$fr->add($hbox);

	my $sw = new Gtk::ScrolledWindow( undef, undef );
	$sw->set_policy( 'never', 'automatic' );
	$sw->set_usize(250, 200);
	$sw->show();

	my $list = Gtk::CList->new_with_titles ( "" );
	$list->set_selection_mode( 'browse' );
	$list->set_shadow_type( 'none' );
	$list->set_reorderable(1);
	$list->set_usize (350, 200);
	$list->signal_connect ('row-move', sub {
		$self->cb_row_move ( @_ ) }
	);
	$list->signal_connect( "select_row", sub { $self->cb_select_filter(@_) } );
	$list->show();

	$sw->add ($list);

	$hbox->pack_start ($sw, 1, 1, 0);
	
	my $vbox = Gtk::VBox->new(0,5);
	$vbox->show;
	
	my $ok_button = Gtk::Button->new( "Ok" );
	$ok_button->show;
	$ok_button->signal_connect('clicked', sub {
		$self->save_selected_filter;
		$win->destroy;
	});
	$vbox->pack_start($ok_button, 0, 1, 1);

	my $add_button = Gtk::Button->new( "Add" );
	$add_button->show;
	$add_button->signal_connect('clicked', sub {
		$self->add_new_filter;
	});
	$vbox->pack_start($add_button, 0, 1, 1);

	my $del_button = Gtk::Button->new( "Delete" );
	$del_button->show;
	$del_button->signal_connect('clicked', sub { $self->cb_delete (@_) } );
	$vbox->pack_start($del_button, 0, 1, 1);

	my $type_radio_input = Gtk::RadioButton->new ("Edit Input Filters");
	$type_radio_input->show;
	$type_radio_input->set_active(1);
	$type_radio_input->signal_connect (
		"clicked", sub {
			$self->debug("switch2 input");
			$self->show_filters(type => 'input')
		}
	);
	$vbox->pack_start($type_radio_input, 0, 0, 0);
	my $type_radio_output = Gtk::RadioButton->new ("Edit Output Filters", $type_radio_input);
	$type_radio_output->show;
	$type_radio_output->signal_connect (
		"clicked", sub {
			$self->debug("switch2 output");
			$self->show_filters(type => 'output')
		}
	);
	$vbox->pack_start($type_radio_output, 0, 0, 0);

	my $text_label = Gtk::Label->new (
		"Filter order is relevant. The first\n".
		"filter which matches will terminate\n".
		"filter evaluation. You can drag and\n".
		"drop filter rows to manipulate the\n".
		"order."
	);
	$text_label->show;
	$text_label->set_line_wrap(1);

	$vbox->pack_start($text_label, 0, 1, 1);

	$hbox->pack_start ($vbox, 0, 0, 0);

	$vpane->add1 ($fr);
	
	my $filter_frame = Gtk::Frame->new ("Edit selected filter");
	$filter_frame->show;
	
	$vpane->add2 ($filter_frame);

	my $folder_menu = $self->comp('folders')->build_menu_of_folders (
		callback => sub {
			my ($folder_id) = @_;
			$self->drop_folder_chosen($folder_id);
		}
	);
	
	$self->gtk_win ($win);
	$self->gtk_filter_list  ($list);
	$self->gtk_filter_frame ($filter_frame);
	$self->gtk_folder_menu ($folder_menu);

	$self->filter_ids([]);

	$self->show_filters ( type => 'input' );

	$self->gtk_window_widget ($win);

	1;
}

sub show_filters {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($type) = @par{'type'};
	
	if ( $self->selected_filter ) {
		$self->save_selected_filter;
	}

	my $list = $self->gtk_filter_list;
	$list->freeze;
	$list->clear;
	$list->set_column_title(
		0,
		$type eq 'input' ? 'Input Filter' : 'Output Filter'
	);

	my $filter_ids = $self->filter_ids([]);

	my $filters = JaM::Filter::IO->list (
		dbh => $self->dbh,
		type => $type,
	);
	foreach my $filter ( @{$filters} ) {
		$list->append($filter->{name});
		push @{$filter_ids}, $filter->{id};
	}

	$list->thaw;

	$self->blank_edit_pane;

	$list->select_row (0, 0);
	
	$self->filter_type($type);

	1;
}

sub cb_row_move {
	my $self = shift; $self->trace_in;
	my ($widget, $from_row, $to_row) = @_;
	
	$self->debug ("from_row=$from_row to_row=$to_row");
	
	my $filter_ids = $self->filter_ids;

	my $from_id = $filter_ids->[$from_row];
	
	if ( $from_row < $to_row ) {
		splice @{$filter_ids}, $to_row+1, 0, $from_id;
		splice @{$filter_ids}, $from_row, 1;
	} else {
		++$from_row;
		splice @{$filter_ids}, $to_row, 0, $from_id;
		splice @{$filter_ids}, $from_row, 1;
	}
	
	JaM::Filter::IO->reorder (
		dbh => $self->dbh,
		filter_ids => $filter_ids
	);
}

sub cb_delete {
	my $self = shift; $self->trace_in;

	my $row = $self->gtk_filter_list->selection;
	return 1 if not defined $row;

	my $filter_ids = $self->filter_ids;
	my $filter_id = $filter_ids->[$row];

	$self->debug("row=$row filter_id=$filter_id");

	my $filter = $self->selected_filter;
	$self->selected_filter(undef);

	$self->gtk_filter_list->remove ($row);
	splice @{$filter_ids}, $row, 1;

	$filter->delete;

	if ( @{$filter_ids} == 0 ) {
		$self->blank_edit_pane;
	} else {
		$row = @{$filter_ids}-1 if $row > @{$filter_ids}-1;
		$self->gtk_filter_list->select_row($row, 0); 
	}

	1;
}

sub blank_edit_pane {
	my $self = shift; $self->trace_in;

	my $frame = $self->gtk_filter_frame;
	my (@children) = $frame->children;
	foreach my $child ( @children ) {
		$frame->remove ($child);
		$child->destroy;
		$child = undef;
	}

	$self->selected_filter(undef);

	1;	
}

sub cb_select_filter {
	my $self = shift; $self->trace_in;
	
	if ( $self->selected_filter ) {
		$self->save_selected_filter;
	}
	
	my $row = $self->gtk_filter_list->selection;
	return 1 if not defined $row;

	my $filter_ids = $self->filter_ids;
	my $filter_id = $filter_ids->[$row];

	return if not defined $filter_id;

	$self->debug("row=$row filter_id=$filter_id");
	
	$self->build_edit_pane ( filter_id => $filter_id );

	1;
}

sub build_edit_pane {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($filter_id) = @par{'filter_id'};

	$self->blank_edit_pane;

	my $filter = JaM::Filter::IO->load (
		dbh => $self->dbh,
		filter_id => $filter_id
	);

	$self->dump($filter);

	$self->selected_filter($filter);

	my $vbox = Gtk::VBox->new(0, 5);
	$vbox->set_border_width(5);
	$vbox->show;
	$self->gtk_filter_frame->add($vbox);
	$self->gtk_filter_vbox ($vbox);

	# Name and action of the filter

	my $table = Gtk::Table->new ( 2, 2, 0 );
	$table->show;
	$table->set_row_spacings ( 2 );
	$table->set_col_spacings ( 2 );
	$table->set_border_width ( 0 );

	my $label = Gtk::Label->new ("Name");
	$label->show;
	my $entry = Gtk::Entry->new;
	$entry->show;
	$table->attach_defaults ($label, 0, 1, 0, 1);
	$table->attach_defaults ($entry, 1, 2, 0, 1);

	$label = Gtk::Label->new ("Action");
	$label->show;

	my $radio_vbox = Gtk::VBox->new (0, 0);
	$radio_vbox->show;
	my $radio1_hbox = Gtk::HBox->new (0, 0);
	$radio1_hbox->show;
	my $radio1 = Gtk::RadioButton->new ("Drop in folder");
	$radio1->show;
	my $radio2 = Gtk::RadioButton->new ("Delete", $radio1);
# currently we support only the drop operation
#	$radio2->show;
	my $folder_entry = Gtk::Entry->new;
	$folder_entry->show;
	$folder_entry->set_text("Click to select folder");
	$folder_entry->set_editable(0);

	$self->gtk_filter_folder ( $folder_entry );

	my $folder_menu = $self->gtk_folder_menu;
	
	$folder_entry->signal_connect('button_press_event', sub {
		my ($widget, $event) = @_;
		$folder_menu->popup (undef, undef, undef, $event->{button});
	});

	$radio1_hbox->pack_start($radio1, 0, 1, 0);
	$radio1_hbox->pack_start($folder_entry, 1, 1, 0);
	$radio_vbox->pack_start($radio1_hbox, 0, 1, 0);
	$radio_vbox->pack_start($radio2, 0, 1, 0);

	$table->attach_defaults ($label, 0, 1, 1, 2);
	$table->attach_defaults ($radio_vbox, 1, 2, 1, 2);

	my $sep = Gtk::HSeparator->new;
	$sep->show;

	my $op_hbox = Gtk::HBox->new (0, 5);
	$op_hbox->show;
	my $op_label = Gtk::Label->new("  Rules are combined with... ");
	$op_label->show;

	my $op_radio_and = Gtk::RadioButton->new ("and");
	$op_radio_and->show;
	my $op_radio_or = Gtk::RadioButton->new ("or", $op_radio_and);
	$op_radio_or->show;
	my $rule_add_button = Gtk::Button->new ("Add new rule");
	$rule_add_button->show;
	$rule_add_button->signal_connect ("clicked", sub { $self->add_new_rule } );
	
	$op_hbox->pack_start($rule_add_button, 0, 1, 0);
	$op_hbox->pack_start($op_label, 0, 1, 0);
	$op_hbox->pack_start($op_radio_and, 0, 1, 0);
	$op_hbox->pack_start($op_radio_or, 0, 1, 0);
	
	$vbox->pack_start($table, 0, 1, 0);
	$vbox->pack_start($sep, 0, 1, 0);
	$vbox->pack_start($op_hbox, 0, 1, 0);

	# fill values
	$entry->set_text ($filter->name);
	if ( $filter->action eq 'drop' ) {
		$radio1->set_active(1);
	} else {
		$radio2->set_active(1);
	}
	if ( $filter->operation eq 'and' ) {
		$op_radio_and->set_active(1);
	} else {
		$op_radio_or->set_active(1);
	}

	if ( $filter->folder_id ) {
		$folder_entry->set_text (
			JaM::Folder->by_id($filter->folder_id)->path
		);
	}

	# connect signals
	$entry->signal_connect ('changed', sub {
		$filter->name($entry->get_text);
		my $list = $self->gtk_filter_list;
		my $row = $list->selection;
		$list->set_text ($row, 0, $filter->name);
	});
	$radio1->signal_connect ('clicked', sub {
		$filter->action ('drop');
	});
	$radio2->signal_connect ('clicked', sub {
		$filter->action ('delete');
	});
	$op_radio_and->signal_connect ('clicked', sub {
		$filter->operation ('and');
	});
	$op_radio_or->signal_connect ('clicked', sub {
		$filter->operation ('or');
	});

	# now the rules
	my $rules = $filter->rules || [];
	foreach my $rule ( @{$rules} ) {
		$self->add_rule ( rule => $rule );
	}

	1;
}

sub drop_folder_chosen {
	my $self = shift; $self->trace_in;
	my ($folder_id) = @_;
	
	my $filter = $self->selected_filter;
	$filter->folder_id($folder_id);
	
	my $text = $self->gtk_filter_folder;
	$text->set_text ( JaM::Folder->by_id($folder_id)->path );
	
	1;
}

sub save_selected_filter {
	my $self = shift; $self->trace_in;
	return if not $self->selected_filter;
	$self->selected_filter->save;
	1;
}

sub add_new_filter {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($folder_object, $mail_object) = @par{'folder_object','mail_object'};
	
	my $name;
	$name = $folder_object->name if $folder_object;
	$name ||= "<New Filter>";

	my $filter = JaM::Filter::IO->create (
		dbh => $self->dbh,
		name => $name,
		type => $self->filter_type,
	);
	
	if ( $folder_object ) {
		$filter->folder_id ( $folder_object->folder_id );
		$filter->save;
	}
	
	my $list = $self->gtk_filter_list;
	$list->append($filter->name);
	push @{$self->filter_ids}, $filter->id;
	
	my $row = scalar(@{$self->filter_ids})-1;
	$list->select_row($row, 0);
	
	$list->moveto( $row, 0, 0.5, 0 ); 
	
	my $value;
	$value = $mail_object->head_get_decoded("to") if $mail_object;

	my $rule = $self->add_new_rule ( value => $value );

	1;
}

sub add_new_rule {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($value) = @par{'value'};

	my $rule = JaM::Filter::IO::Rule->create (
                field => 'tofromcc',
                operation => 'contains',
                value => $value,
	);
	
	$self->selected_filter->append_rule ( rule => $rule )->save;
	
	$self->add_rule ( rule => $rule );
	
	return $rule;
}

sub add_rule {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($rule) = @par{'rule'};

	my $hbox = Gtk::HBox->new (0, 5);
	$hbox->show;
	
	my $possible_fields = $rule->possible_fields;
	my $possible_ops    = $rule->possible_operations;

	my ($value, $name);
	my $i = 0;
	my $selected = 0;
	my $selected_value = $rule->field;
	my $fields_menu = Gtk::Menu->new;
	foreach $value ( sort keys %{$possible_fields} ) {
		$name = $possible_fields->{$value};
		my $item = Gtk::MenuItem->new ($name);
		$item->show;
		$item->signal_connect ("activate", sub { $rule->field($_[1]) }, $value);
		$fields_menu->append ($item);
		$selected = $i if $selected_value eq $value;
		++$i;
	}
	my $fields_options = Gtk::OptionMenu->new;
	$fields_options->set_menu($fields_menu);
	$fields_options->show;
	$fields_options->set_history ($selected);

	$hbox->pack_start($fields_options, 0, 0, 0);

	$i = 0;
	$selected = 0;
	$selected_value = $rule->operation;
	my $ops_menu = Gtk::Menu->new;
	foreach $value ( sort keys %{$possible_ops} ) {
		$name = $possible_ops->{$value};
		my $item = Gtk::MenuItem->new ($name);
		$item->show;
		$item->signal_connect ("activate", sub { $rule->operation($_[1]) }, $value);
		$ops_menu->append ($item);
		$selected = $i if $selected_value eq $value;
		++$i;
	}
	my $ops_options = Gtk::OptionMenu->new;
	$ops_options->set_menu($ops_menu);
	$ops_options->show;
	$ops_options->set_history ($selected);

	$hbox->pack_start($ops_options, 0, 0, 0);

	my $value_entry = Gtk::Entry->new;
	$value_entry->show;
	$value_entry->set_text ( $rule->value );
	$value_entry->signal_connect ('changed', sub { $rule->value($_[0]->get_text) } );

	$hbox->pack_start($value_entry, 1, 1, 0);

	my $del_button = Gtk::Button->new (" Del ");
	$del_button->show;
	$del_button->signal_connect ( "clicked", sub {
		$self->del_rule (
			filter => $self->selected_filter,
			rule => $rule,
			hbox => $hbox
		);
	} );
	$hbox->pack_start($del_button, 0, 0, 0);

	$self->gtk_filter_vbox->pack_start($hbox, 0, 0, 0);

	1;
}

sub del_rule {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($rule, $hbox, $filter) = @par{'rule','hbox','filter'};
	
	$self->gtk_filter_vbox->remove($hbox);
	$filter->remove_rule ( rule => $rule );
	
	1;
}

1;