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


# $Id: Search.pm,v 1.2 2001/09/08 14:33:19 joern Exp $

package JaM::GUI::Search;

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

use strict;
use JaM::GUI::Window;
use JaM::GUI::Subjects;
use JaM::GUI::IO_Filter;

sub multi_instance_window  { 1 }

sub search_rules	{ my $s = shift; $s->{search_rules}
		          = shift if @_; $s->{search_rules}	}
sub search_operation	{ my $s = shift; $s->{search_operation}
		          = shift if @_; $s->{search_operation}	}
sub search_folder_id	{ my $s = shift; $s->{search_folder_id}
		          = shift if @_; $s->{search_folder_id}	}
sub search_recursive	{ my $s = shift; $s->{search_recursive}
		          = shift if @_; $s->{search_recursive}	}

sub build {
	my $self = shift;
	
	# build subjects widget
	$self->SUPER::build (
		without_quick_search => 1,
		without_resize_tracking => 1,
	);
	
	my $win = Gtk::Window->new;
	$win->set_position ("center");
	$win->set_title ("Advanced Search");
	$win->border_width(3);
	$win->set_default_size (530, 500);
	$win->realize;
	$win->show;

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

	my $folder_hbox = Gtk::HBox->new (0,5);
	$folder_hbox->show;

	my $folder_menu = $self->comp('folders')->build_menu_of_folders (
		callback => sub {
			my ($folder_id) = @_;
			$self->folder_chosen($folder_id);
		}
	);

	my $folder_label = Gtk::Label->new ("Search in folder");
	$folder_label->show;
	
	my $folder_entry = Gtk::Entry->new;
	$folder_entry->show;
	$folder_entry->set_text("Click to select folder");
	$folder_entry->set_editable(0);
	$folder_entry->set_usize(200, 20);
	$folder_entry->signal_connect('button_press_event', sub {
		my ($widget, $event) = @_;
		$folder_menu->popup (undef, undef, undef, $event->{button});
	});

	my $folder_recursive = Gtk::CheckButton->new ("Include subfolders");
	$folder_recursive->show;
	$folder_recursive->set_active(0);
	$folder_recursive->signal_connect ('clicked', sub {
		$self->search_recursive ( $folder_recursive->get_active );
	});
	
	my $search_button = Gtk::Button->new (" Start Query ");
	$search_button->show;
	$search_button->signal_connect ("clicked", sub { $self->start_query } );

	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 } );

	$self->search_operation ('and');

	$op_radio_and->signal_connect ('clicked', sub {
		$self->search_operation ('and');
	});
	$op_radio_or->signal_connect ('clicked', sub {
		$self->search_operation ('or');
	});
	
	$folder_hbox->pack_start($folder_label, 0, 0, 0);
	$folder_hbox->pack_start($folder_entry, 0, 0, 0);
	$folder_hbox->pack_start($folder_recursive, 0, 0, 0);
	$folder_hbox->pack_start($search_button, 0, 0, 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);
	$op_hbox->pack_start($rule_add_button, 0, 1, 0);

	my $filter_vbox = Gtk::VBox->new (0,5);
	$filter_vbox->show;

	my $filter_sw = Gtk::ScrolledWindow->new;
	$filter_sw->set_usize (undef, 100);
	$filter_sw->set_policy ('never','automatic');
	$filter_sw->show;
	$filter_sw->add_with_viewport($filter_vbox);

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

	my $top_vbox = Gtk::VBox->new(0,5);
	$top_vbox->show;
	$top_vbox->pack_start($folder_hbox, 0, 0, 0);
	$top_vbox->pack_start($hsep, 0, 0, 0);
	$top_vbox->pack_start($op_hbox, 0, 0, 0);
	$top_vbox->pack_start($filter_sw, 1, 1, 0);

	$vpane->add1 ($top_vbox);
	$vpane->add2 ($self->gtk_subjects);
	
	# destroy the handlers which track resizing of the widget
	$self->gtk_subjects->signal_handlers_destroy;
#	$self->gtk_subjects_list->signal_handlers_destroy;
	$self->gtk_subjects_list->set_column_width (
		1, $self->config('subjects_column_1') * 0.6
	);
	$self->gtk_subjects_list->signal_connect(
		'button_press_event', sub { $self->cb_click_subjects(@_) }
	);

	$self->gtk_filter_folder ( $folder_entry );
	$self->gtk_window_widget($win);
	$self->gtk_filter_vbox($filter_vbox);
	$self->gtk_folder_menu ($folder_menu);

	$self->search_rules ([]);
	$self->add_new_rule;

	$self->folder_chosen(1);

	return $win;
}

sub add_new_rule {
	my $self = shift;
	
	my $rule = JaM::Filter::Search::Rule->create (
		dbh => $self->dbh,
                field => 'tofromcc',
                operation => 'contains',
                value => "",
	);
	
	push @{$self->search_rules}, $rule;

	$self->add_rule ( rule => $rule );
	
	1;
}

sub del_rule {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($rule, $hbox, $filter) = @par{'rule','hbox','filter'};
	
	my $i=0;
	foreach my $r ( @{$self->search_rules} ) {
		last if ( $r eq $rule );
		++$i;
	}
	
	splice @{$self->search_rules}, $i, 1;
	
	$self->gtk_filter_vbox->remove($hbox);
	
	1;
}

sub folder_chosen {
	my $self = shift; $self->trace_in;
	my ($folder_id) = @_;
	
	my $text = $self->gtk_filter_folder;
	$text->set_text ( JaM::Folder->by_id($folder_id)->path );
	
	$self->search_folder_id ( $folder_id);

	1;
}

sub start_query {
	my $self = shift;

	return 1 if not @{$self->search_rules};

	my $operation = $self->search_operation;
	my $folder_id = $self->search_folder_id;
	my $recursive = $self->search_recursive;

	my @where;
	my @tables = ("Mail M");

	if ( $folder_id and $recursive) {
		if ( $folder_id != 1 ) {
			push @tables, "Folder F";
			push @where,
				"(F.path like '".
				 JaM::Folder->by_id($folder_id)->path.'/%'.
				 "' or M.folder_id=$folder_id) and M.folder_id = F.id ";
		}
	} elsif ( $folder_id ) {
		push @where, "M.folder_id=$folder_id";
	}
	
	my $code;
	my @op_where;
	my $with_entity;
	foreach my $rule ( @{$self->search_rules} ) {
		$code = $rule->code;
		push @op_where, "($code)";
		$with_entity = 1 if $code =~ /E\./;
	}
	
	if ( $with_entity ) {
		push @tables, "Entity E";
		push @where, "M.id = E.mail_id";
	}
	
	my $where = join (" and ", @where)." and " if @where;
	my $sql = "
		   		   select M.id, M.status, M.subject, M.sender,
		 	  		 	  UNIX_TIMESTAMP(M.date)
		   		   from   ".join (", ", @tables)."
		   		   where  $where
		   	  		   	  (".join (" $operation ", @op_where).")
		   		   order by 5 desc\n";

print STDERR "$sql\n";

	$self->show ( sql => $sql );
	
	1;
}

sub cb_select_mail {
	my $self = shift; $self->trace_in;
	my ($clist, $row, $column, $event) = @_;
	
	return 1 if not defined $self->mail_ids;
	my @sel = $self->gtk_subjects_list->selection;
	return if @sel > 1;

	# determine selected mail id
	my $mail_id = $self->mail_ids->[$row];
	
	$self->debug ("column=$column mail_id=$mail_id selected_id=".$self->selected_mail_id);
	
	# nothing todo if this mail is already selected
	return 1 if $self->selected_mail_id == $mail_id;

	if ( $self->selected_mail_id != $mail_id ) {
		$self->selected_mail_id ( $mail_id );
		$self->comp('mail')->show ( mail_id => $mail_id );
	}

	1;
}

package JaM::Filter::Search::Rule;

use vars qw (@ISA);
@ISA = qw ( JaM::Filter::IO::Rule );

my %operations = (
	"contains"  	     =>    "Contains",
	"contains!" 	     =>    "Does'n contain",
);

sub dbh { shift->{dbh}	}

sub create {
	my $type = shift;
	my %par = @_;
	my ($dbh) = @par{'dbh'};
	
	my $self = $type->SUPER::create (@_);

	$self->{dbh} = $dbh;
	
	$self->calculate_code;
	
	return bless $self, $type;
}

sub possible_operations {
	return \%operations;
}

sub calculate_code {
	my $self = shift;

	my $field     = $self->field;
	my $op        = $self->operation;
	my $value     = $self->value;

	# in construction phase our $dbh is not set
	return if not $self->dbh;

	$op = $op eq 'contains' ? "like" : "not like";
	$value = $self->dbh->quote('%'.$value.'%');

	my @fields;
	push @fields, "M.head_to" if $field =~ /to/;
	push @fields, "M.sender"  if $field =~ /from/;
	push @fields, "M.head_cc" if $field =~ /cc/;
	push @fields, "M.subject" if $field eq 'subject';
	push @fields, "E.data"    if $field eq 'body';

	my $code;
	foreach my $f ( @fields ) {
		$code .= "$f $op $value or ";
	}
	
	$code =~ s/or $//;
	
	return $self->code($code);	
}

1;