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


# $Id: Folders.pm,v 1.22 2002/03/03 13:16:05 joern Exp $

package JaM::GUI::Folders;

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

use strict;
use JaM::GUI::Component;
use JaM::GUI::Compose;
use JaM::Folder;

my $DEBUG = 0;

my @closed_xpm = ( "16 16 6 1",
                  "       c None s None",
                  ".      c black",
                  "X      c #a0a0a0",
                  "o      c yellow",
                  "O      c #808080",
                  "#      c white",
                  "                ",
                  "       ..       ",
                  "     ..XX.      ",
                  "   ..XXXXX.     ",
                  " ..XXXXXXXX.    ",
                  ".ooXXXXXXXXX.   ",
                  "..ooXXXXXXXXX.  ",
                  ".X.ooXXXXXXXXX. ",
                  ".XX.ooXXXXXX..  ",
                  " .XX.ooXXX..#O  ",
                  "  .XX.oo..##OO. ",
                  "   .XX..##OO..  ",
                  "    .X.#OO..    ",
                  "     ..O..      ",
                  "      ..        ",
                  "                ");
my @open_xpm = ( "16 16 4 1",
                "       c None s None",
                ".      c black",
                "X      c #808080",
                "o      c white",
                "                ",
                "  ..            ",
                " .Xo.    ...    ",
                " .Xoo. ..oo.    ",
                " .Xooo.Xooo...  ",
                " .Xooo.oooo.X.  ",
                " .Xooo.Xooo.X.  ",
                " .Xooo.oooo.X.  ",
                " .Xooo.Xooo.X.  ",
                " .Xooo.oooo.X.  ",
                "  .Xoo.Xoo..X.  ",
                "   .Xo.o..ooX.  ",
                "    .X..XXXXX.  ",
                "    ..X.......  ",
                "     ..         ",
                "                ");
my @leaf_xpm = ( "16 16 4 1",
                "       c None s None",
                ".      c black",
                "X      c white",
                "o      c #808080",
                "                ",
                "   .......      ",
                "   .XXXXX..     ",
                "   .XoooX.X.    ",
                "   .XXXXX....   ",
                "   .XooooXoo.o  ",
                "   .XXXXXXXX.o  ",
                "   .XooooooX.o  ",
                "   .XXXXXXXX.o  ",
                "   .XooooooX.o  ",
                "   .XXXXXXXX.o  ",
                "   .XooooooX.o  ",
                "   .XXXXXXXX.o  ",
                "   ..........o  ",
                "    oooooooooo  ",
                "                ");

@leaf_xpm = @open_xpm = @closed_xpm;

my ($closed_pix, $closed_mask);
my ($opened_pix, $opened_mask);
my ($leaf_pix,   $leaf_mask);

# get/set selected Folder
sub selected_folder_object	{ my $s = shift; $s->{selected_folder_object	}
		          	  = shift if @_; $s->{selected_folder_object}	}

# get/set selected Folder object on which a Popup is requested
sub popup_folder_object	{ my $s = shift; $s->{popup_folder_object}
		          = shift if @_; $s->{popup_folder_object}		}

# get/set selected row on which a Popup is requested
sub popup_row           { my $s = shift; $s->{popup_row}
		          = shift if @_; $s->{popup_row}			}

# get/set list ref of folder gtk items
sub gtk_folder_items	{ my $s = shift; $s->{gtk_folder_items}
		          = shift if @_; $s->{gtk_folder_items}			}

# get/set gtk object for folder scrollable window
sub gtk_folders		{ my $s = shift; $s->{gtk_folders}
		          = shift if @_; $s->{gtk_folders}			}

# get/set gtk object for folder ctree
sub gtk_folders_tree	{ my $s = shift; $s->{gtk_folders_tree}
		          = shift if @_; $s->{gtk_folders_tree}			}

# get/set gtk style for folder without new mails
sub gtk_read_style	{ my $s = shift; $s->{gtk_read_style}
		          = shift if @_; $s->{gtk_read_style}			}

# get/set gtk style for folder with unread mails
sub gtk_unread_style	{ my $s = shift; $s->{gtk_unread_style}
		          = shift if @_; $s->{gtk_unread_style}			}

# get/set gtk style for folder with unread child folders
sub gtk_unread_child_style { my $s = shift; $s->{gtk_unread_child_style}
		             = shift if @_; $s->{gtk_unread_child_style}			}

# popup menu separator for "Create Template"
sub gtk_template_sep	{ my $s = shift; $s->{gtk_template_sep}
		          = shift if @_; $s->{gtk_template_sep}			}

# popup menu item for "Create Template"
sub gtk_template_item	{ my $s = shift; $s->{gtk_template_item}
		          = shift if @_; $s->{gtk_template_item}		}

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

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

# helper method for setting up pixmaps
sub initialize_pixmap {
	my $self = shift; $self->trace_in;
	my @xpm = @_;

	my ($pixmap, $mask);
	my $win   = $self->gtk_win;
	my $style = $win->get_style()->bg( 'normal' );

	return ($pixmap, $mask) = Gtk::Gdk::Pixmap->create_from_xpm_d (
		$win->window, $style, @xpm
	);
}

# build scrolled window for folder ctree
sub build {
	my $self = shift; $self->trace_in;
	
	JaM::Folder->init ( dbh => $self->dbh );
	
	my $folders = new Gtk::ScrolledWindow (undef, undef);
	$folders->set_policy ('automatic', 'automatic');
	$folders->set_usize($self->config('folders_width'), undef);

	$folders->signal_connect("size-allocate",
		sub { $self->config('folders_width', $_[1]->[2]) }
	);

	# Set up Pixmaps
	($closed_pix, $closed_mask) = $self->initialize_pixmap( @closed_xpm );
	($opened_pix, $opened_mask) = $self->initialize_pixmap( @open_xpm );
	($leaf_pix,   $leaf_mask)   = $self->initialize_pixmap( @leaf_xpm );

	my $root_tree = Gtk::CTree->new_with_titles (
		0, 'Name','Unread','Total'
	);

	$root_tree->signal_connect("resize-column",
		sub {
			$self->config('folders_column_'.$_[1], $_[2]);
		}
	);

	$root_tree->set_column_width (0, $self->config('folders_column_0'));
	$root_tree->set_column_width (1, $self->config('folders_column_1'));
	$root_tree->set_column_width (2, $self->config('folders_column_2'));
	$root_tree->set_reorderable(1);
	$root_tree->set_line_style ('dotted');
#	$root_tree->set_user_data ($self);
	$root_tree->signal_connect ('select_row', sub { $self->cb_folder_select(@_) } );
	$root_tree->signal_connect ('tree-expand', sub {
		$self->cb_tree_click ( type => 'expand', tree => $_[0], node => $_[1] ) }
	);
	$root_tree->signal_connect ('tree-collapse', sub {
		$self->cb_tree_click ( type => 'collapse', tree => $_[0], node => $_[1] ) }
	);
	$root_tree->signal_connect ('tree-move', sub {
		$self->cb_tree_move ( @_ ) }
	);

	$root_tree->set_selection_mode( 'browse' );
	$folders->add_with_viewport($root_tree);
	$root_tree->show;

	# build tree
	my $unread_style = $root_tree->style->copy;
	$unread_style->font($self->config('font_folder_unread'));
	my $read_style = $root_tree->style->copy;
	$read_style->font($self->config('font_folder_read'));
	my $unread_child_style = $root_tree->style->copy;
	$unread_child_style->font($self->config('font_folder_unread'));
	$unread_child_style->fg('normal',$self->gdk_color($self->config('folder_unread_child_color')));

	$self->gtk_unread_style ($unread_style);
	$self->gtk_unread_child_style ($unread_child_style);
	$self->gtk_read_style   ($read_style);

	$self->gtk_folder_items ( {} );
	$self->gtk_folders ($folders);
	$self->gtk_folders_tree ($root_tree);

	$self->add_tree (
		tree      => $root_tree,
		parent_id => 1
	);

	$folders->show;

	$root_tree->signal_connect('button_press_event', sub { $self->cb_click_clist(@_) } );

	# now build popup Menu
	my $popup = $root_tree->{popup} = Gtk::Menu->new;
	my $item;

	$item = Gtk::MenuItem->new ("Advanced Search...");
	$popup->append($item);
	$item->signal_connect ("activate", sub {$self->cb_search_in_folder ( @_ ) } );
	$item->show;

	$item = Gtk::MenuItem->new;
	$popup->append($item);
	$item->show;

	$item = Gtk::MenuItem->new ("Rename Folder...");
	$popup->append($item);
	$item->signal_connect ("activate", sub { $self->cb_rename_folder ( @_ ) } );
	$item->show;
	$item = Gtk::MenuItem->new ("Create New Folder...");
	$popup->append($item);
	$item->signal_connect ("activate", sub { $self->cb_create_folder ( @_ ) } );
	$item->show;
	$item = Gtk::MenuItem->new ("Delete Folder...");
	$popup->append($item);
	$item->signal_connect ("activate", sub { $self->cb_delete_folder ( @_ ) } );
	$item->show;
	
	$item = Gtk::MenuItem->new;
	$popup->append($item);
	$item->show;

	$item = Gtk::MenuItem->new ("Add Input Filter...");
	$popup->append($item);
	$item->signal_connect ("activate", sub { $self->cb_add_input_filter ( @_ ) } );
	$item->show;

	$item = Gtk::MenuItem->new;
	$popup->append($item);
	$item->show;

	my $ignore_reply_to_item = Gtk::RadioMenuItem->new_with_label ("Ignore Reply-To Header");
	$popup->append($ignore_reply_to_item);
	$ignore_reply_to_item->signal_connect ("activate", sub { $self->cb_ignore_reply_to ( @_ ) } );
	$ignore_reply_to_item->show;

	$self->gtk_ignore_reply_to  ($ignore_reply_to_item);

	$item = Gtk::RadioMenuItem->new_with_label ("Don't ignore Reply-To Header", $ignore_reply_to_item);
	$popup->append($item);
	$item->signal_connect ("activate", sub { $self->cb_dont_ignore_reply_to ( @_ ) } );
	$item->show;

	$self->gtk_dont_ignore_reply_to  ($item);

	$item = Gtk::MenuItem->new;
	$popup->append($item);

	$self->gtk_template_sep ( $item );

	$item = Gtk::MenuItem->new ("Create New Template...");
	$popup->append($item);
	$item->signal_connect ("activate", sub { $self->cb_create_new_template ( @_ ) } );

	$self->gtk_template_item ( $item );

	$self->widget ($folders);

	$self->update_folder_stati;

	return $self;
}

sub add_tree {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my  ($tree, $parent_id) =
	@par{'tree','parent_id'};

	my $folders_href = JaM::Folder->query ( 
		dbh => $self->dbh,
		where => "parent_id = ?",
		params => [ $parent_id ]
	);

	my $folder_items = $self->gtk_folder_items;
	
	# build sibling hash
	my %sibling;
	for ( keys %{$folders_href} ) {
		$folders_href->{$_}->save; # recalculate path
		$sibling{$folders_href->{$_}->sibling_id} = $folders_href->{$_};
	}

	# we start with the folder, which has no sibling
	my $sibling_id = 99999;
	my $max = scalar(keys(%sibling));

	my ($folder, $sibling_item, $item);
	for (my $i=0; $i < $max; ++$i) {
		$folder = $sibling{$sibling_id};
		$sibling_item = $folder_items->{$sibling_id}
			if $sibling_id != 99999;
		
		$self->insert_folder_item (
			folder_object => $folder,
			sibling_item => $sibling_item,
		);

		$sibling_id = $folder->id;
	}

	1;
}

sub insert_folder_item {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my  ($folder_object, $sibling_item) =
	@par{'folder_object','sibling_item'};
	
	my $folder_items = $self->gtk_folder_items;
	my $tree = $self->gtk_folders_tree;
	my $parent_id = $folder_object->parent_id;
	
	my $item = $folder_items->{$folder_object->id} = $tree->insert_node (
		$folder_items->{$parent_id},
		$sibling_item,
		[ $folder_object->name,
		  $folder_object->mail_sum -
		  $folder_object->mail_read_sum,
		  $folder_object->mail_sum ],
		5,
		$closed_pix, $closed_mask,
		$opened_pix, $opened_mask,
		0, ($folder_object->leaf ? 1 : $folder_object->opened)
	);

	$self->add_tree (
		tree => $tree,
		parent_id => $folder_object->id
	) if not $folder_object->leaf;

	$item->{folder_id} = $folder_object->id;

	$tree->node_set_row_style(
		$item, ($folder_object->mail_read_sum < $folder_object->mail_sum) ?
		       $self->gtk_unread_style : $self->gtk_read_style
	);
	
	return $item;
}

sub cb_click_clist {
	my $self = shift;
	my ($widget, $event) = @_;

	my ( $row, $column ) = $widget->get_selection_info( $event->{x}, $event->{y} );

	$self->popup_folder_object (
		JaM::Folder->by_id($widget->node_nth( $row )->{folder_id})
	);
	$self->popup_row ($widget->node_nth( $row ));

	if ( $event->{button} == 3 and $widget->{'popup'} ) {
		if ( $self->popup_folder_object->folder_id ==
		     $self->config('templates_folder_id') ) {
			$self->gtk_template_sep->show;
			$self->gtk_template_item->show;
		} else {
			$self->gtk_template_sep->hide;
			$self->gtk_template_item->hide;
		}
		if ( $self->popup_folder_object->ignore_reply_to ) {
			$self->gtk_ignore_reply_to->set_active(1);
		} else {
			$self->gtk_dont_ignore_reply_to->set_active(1);
		}
		$widget->{'popup'}->popup(undef,undef,$event->{button},1);
	}

	1;
}

sub cb_rename_folder {
	my $self = shift;

	my $folder_object = $self->popup_folder_object;
	my $name = $folder_object->name;

	my $dialog;
	$dialog = $self->folder_dialog (
		title => "Rename Folder",
		label => "Enter new name for folder '$name'",
		value => $name,
		cb => sub {
			my ($text) = @_;
			return $self->rename_folder (
				folder_object => $folder_object,
				name => $text->get_text,
			);
		}
	);

	1;
}

sub cb_search_in_folder {
	my $self = shift;

	my $folder_object = $self->popup_folder_object;
	
  	require JaM::GUI::Search;
  	my $search = JaM::GUI::Search->new (
		dbh => $self->dbh
	);
	$search->open_window;
	
	$search->folder_chosen ($folder_object->id);
	
	1;
}

sub cb_create_new_template {
	my $self = shift;
	
	my $compose = JaM::GUI::Compose->new (
		dbh => $self->dbh
	);
	
	$compose->save_as_template(1);

	$compose->build;
	
	1;
}

sub folder_dialog {
	my $self = shift;
	my %par = @_;
	my ($title, $label_text, $value, $cb) = @par{'title','label','value','cb'};

	my $dialog = Gtk::Dialog->new;
	$dialog->border_width(10);
	$dialog->set_position('mouse');
	$dialog->set_title ($title);

	my $label = Gtk::Label->new ($label_text);
	$dialog->vbox->pack_start ($label, 1, 1, 0);
	$label->show;
	
	my $text = Gtk::Entry->new ( 40 );
	$dialog->vbox->pack_start ($text, 1, 1, 0);
	$text->set_text ( $value );
	$text->show;
	
	my $ok = new Gtk::Button( "Ok" );
	$dialog->action_area->pack_start( $ok, 1, 1, 0 );
	$ok->signal_connect( "clicked", sub {
		&$cb($text) && $dialog->destroy;
	} );
	$ok->show();

	my $cancel = new Gtk::Button( "Cancel" );
	$dialog->action_area->pack_start( $cancel, 1, 1, 0 );
	$cancel->signal_connect( "clicked", sub { $dialog->destroy } );
	$cancel->show();
	
	$dialog->show;
	
	return $dialog;
}

sub rename_folder {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($folder_object, $name) = @par{'folder_object','name'};
	
	$self->debug ("folder_id=".$folder_object->id.", name=$name");
	
	return if not $name;

	my $childs_with_same_name = JaM::Folder->query (
		where => "parent_id = ? and name=?",
		params => [ $folder_object->parent_id, $name ]
	);
	
	if ( keys %{$childs_with_same_name} ) {
		$self->message_window (
			message => "A folder with this name already exists."
		);
		return;
	}
	
	$folder_object->name($name);
	$folder_object->save;
	
	$self->update_folder_item ( folder_object => $folder_object );
	
	1;
}

sub cb_create_folder {
	my $self = shift;

	my $folder_object = $self->popup_folder_object;

	my $dialog;
	$dialog = $self->folder_dialog (
		title => "Create Folder",
		label => "Enter name for the new folder",
		value => "",
		cb => sub {
			my ($text) = @_;
			return $self->create_folder (
				parent_folder_object => $folder_object,
				name => $text->get_text,
			);
		}
	);

	1;
}

sub cb_tree_click {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my ($type, $tree, $node) = @par{'type','tree','node'};
	
	my $opened = $type eq 'expand' ? 1 : 0;
	my $folder_object = JaM::Folder->by_id($node->{folder_id});
	
	$folder_object->opened($opened);
	$folder_object->save;

	1;
}

# callback for folder selection
sub cb_folder_select {
	my $self = shift; $self->trace_in;
	my ($ctree, $row) = @_;

	my $node = $ctree->node_nth( $row );
	my $folder_object = JaM::Folder->by_id($node->{folder_id});

	$self->selected_folder_object ( $folder_object );

	$self->comp('mail')->no_status_change_on_show(1);
	$self->comp('subjects')->show (
		folder_object => $folder_object,
	);
	$self->comp('mail')->no_status_change_on_show(0);

	my $gui = $self->comp('gui');
	$gui->no_subjects_update (1);
	$gui->update_folder_limit (
		folder_object => $folder_object
	);
	$gui->no_subjects_update (0);
	
	1;
}

# update ctree item for a specific folder from database
sub update_folder_item {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my  ($folder_object, $no_folder_stati) =
	@par{'folder_object','no_folder_stati'};

	$folder_object ||= $self->selected_folder_object;

	$self->debug ("folder_id=".$folder_object->id);

	my $name = $folder_object->name;

	my $item = $self->gtk_folder_items->{$folder_object->id};

	my $widget = $self->gtk_folders_tree;

	my ($text, $spacing, $pixmap, $mask) = $widget->node_get_pixtext( $item, 0 );
	if ( $name ne $text ) {	
		$self->debug ("folder name changed: old=$text new=$name");
		$widget->node_set_pixtext( $item, 0, $name, $spacing, $pixmap, $mask );
	}
	
	my ($mail_sum, $mail_read_sum) = ($folder_object->mail_sum,
					  $folder_object->mail_read_sum);

	$self->debug ("mail_sum=$mail_sum, mail_read_sum=$mail_read_sum");

	$widget->set_text( $item, 1, $mail_sum -
				     $mail_read_sum); 
	$widget->set_text( $item, 2, $mail_sum); 

	$widget->node_set_row_style(
		$item, ($mail_read_sum < $mail_sum) ?
		       $self->gtk_unread_style : $self->gtk_read_style
	);
	
	$self->update_folder_stati
		if not $no_folder_stati;

	1;
}

sub update_folder_stati {
	my $self = shift; $self->trace_in;
	
	$self->debug ("updating folder read/unread stati");
	
	JaM::Folder->recalculate_folder_stati ( dbh => $self->dbh );

	my $folder_items = $self->gtk_folder_items;
	my $folders_tree = $self->gtk_folders_tree;

	my $all_folders = JaM::Folder->all_folders;

	my ($folder_id, $folder, $status, $style);
	while ( ($folder_id, $folder) = each %{$all_folders} ) {
		next if $folder_id == 1;
		$status = $folder->status;
		if ( $folder_items->{$folder_id}->{status} ne $status ) {
			$style = $self->gtk_read_style;
			$style = $self->gtk_unread_style if $status eq 'N';
			$style = $self->gtk_unread_child_style if $status eq 'NC';
			$folders_tree->node_set_row_style(
				$folder_items->{$folder_id}, $style
			);
			$folder_items->{$folder_id}->{status} = $status;
		}
	}
	
	1;
}

sub create_folder {
	my $self = shift; $self->trace_in;
	my %par = @_;
	my  ($parent_folder_object, $name) =
	@par{'parent_folder_object','name'};
	
	my $parent_id = $parent_folder_object->id;
	
	$self->debug ("parent_id=$parent_id name=$name");
	
	# check if name clashes
	my $childs_with_same_name = JaM::Folder->query (
		where => "parent_id = ? and name=?",
		params => [ $parent_folder_object->id, $name ]
	);
	
	if ( keys %{$childs_with_same_name} ) {
		$self->message_window (
			message => "A folder with this name already exists."
		);
		return;
	}
	
	my $folder_items = $self->gtk_folder_items;
	my $folders_tree = $self->gtk_folders_tree;
	
	my $parent_item = $folder_items->{$parent_folder_object->id};
	my $sibling_item;
	my $sibling_folder_object;

	if ( $parent_folder_object->leaf ) {
		$parent_folder_object->leaf (0); 
		$parent_folder_object->save;

	} else {
		my $sibling_id = $parent_folder_object->get_first_child_folder_id;
		$self->debug("sibling_id=$sibling_id");
		if ( $sibling_id ) {
			$sibling_item = $folder_items->{$sibling_id};
			$sibling_folder_object = JaM::Folder->by_id($sibling_id);
		}
	}
	
	my $new_folder = JaM::Folder->create (
		dbh => $self->dbh,
		name => $name,
		parent => $parent_folder_object,
		sibling => $sibling_folder_object
	);

	my $item = $self->insert_folder_item (
		folder_object => $new_folder,
		sibling_item => $sibling_item,
	);

	1;
}

sub cb_tree_move {
	my $self = shift;
	my ($ctree, $moved, $parent, $sibling) = @_;

	my $moved_object  = JaM::Folder->by_id($moved->{folder_id});

	my $sibling_object;
	$sibling_object   = JaM::Folder->by_id($sibling->{folder_id}) if $sibling;

	my $parent_object;
	$parent_object = JaM::Folder->by_id($parent->{folder_id}) if $parent;
	$parent_object = JaM::Folder->by_id(1) if not $parent;

	$self->move_tree (
		moved_object   => $moved_object,
		parent_object  => $parent_object,
		sibling_object => $sibling_object,
	);
	
	1;
}

sub move_tree {
	my $self = shift;
	my %par = @_;

	my  ($moved_object, $parent_object, $sibling_object) =
	@par{'moved_object','parent_object','sibling_object'};
	
	$self->debug("moved_object");$self->dump ($moved_object);
	$self->debug("parent_object");$self->dump ($parent_object);
	$self->debug("sibling_object");$self->dump ($sibling_object);
	
	
	# rename folder if name clashes with siblings in the new folder
	while (1) {
		my $childs_with_same_name = JaM::Folder->query (
			where => "parent_id = ? and name=? and id != ?",
			params => [ $parent_object->id, $moved_object->name,
				    $moved_object->id ]
		);
		if  ( keys %{$childs_with_same_name} ) {
			my $name = $moved_object->name;
			$name =~ s/(\d+)?$/$1+1/e;
			$moved_object->name($name);			
		} else {
			last;
		}
	}
	
	my $folder_items  = $self->gtk_folder_items;
	
	# is the parent_object a leaf? change that!
	if ( $parent_object->leaf ) {
		$parent_object->leaf(0);
		$parent_object->save;
	}
	
	# if we was the last child, tell our parent,
	# that now it is a leaf
	$self->debug ("sibling_id=".$moved_object->sibling_id." sibling_of_id=",$moved_object->sibling_of_id);
	if ( $moved_object->sibling_id == 99999 and not $moved_object->sibling_of_id ) {
		my $my_parent_object = JaM::Folder->by_id($moved_object->parent_id);
		$my_parent_object->leaf(1);
		$my_parent_object->save;
	}

	# First remove the moved item
	# We have to handle two cases:
	# - it has a sibling
	# - it has no sibling
	if ( $moved_object->sibling_id == 99999 ) {
		# no sibling - now the object we are sibling of
		# will have no sibling anymore
		my $sibling_of_id = $moved_object->sibling_of_id;
		if ( $sibling_of_id ) {
			my $sibling_of = JaM::Folder->by_id($sibling_of_id);
			$sibling_of->sibling_id(99999);
			$sibling_of->save;
		}
	} else {
		# ok, we have a sibling. connect it to the object,
		# we are sibling of
		my $sibling_of_id = $moved_object->sibling_of_id;
		if ( $sibling_of_id ) {
			my $sibling_of = JaM::Folder->by_id($sibling_of_id);
			my $my_sibling = JaM::Folder->by_id($moved_object->sibling_id);
			$sibling_of->sibling_id($my_sibling->id);
			$sibling_of->save;
		}
	}

	# Now place the moved item
	# Again we have two cases:
	# - we'll have a sibling
	# - we'll have no sibling
	if ( $sibling_object ) {
		# ok, we'll have a sibling
		my $sibling_of_id = $sibling_object->sibling_of_id;
		if ( $sibling_of_id ) {
			my $sibling_of = JaM::Folder->by_id($sibling_of_id);
			$sibling_of->sibling_id ($moved_object->id);
			$sibling_of->save;
		}
		$moved_object->sibling_id($sibling_object->id);
	} else {
		# we'll have no sibling
		my $last_folder_id = $parent_object->get_last_child_folder_id;
		$self->debug("last_folder_id=$last_folder_id");
		if ( $last_folder_id ) {
			my $sibling_of = JaM::Folder->by_id($last_folder_id);
			$sibling_of->sibling_id($moved_object->id);
			$sibling_of->save;
		}
		$moved_object->sibling_id(99999);
	}

	# set parent_id (this computes the new path also)
	$moved_object->parent_id ($parent_object->id);
	
	# save
	$moved_object->save;

	# update item (may be it has been renamed)
	$self->update_folder_item ( folder_object => $moved_object );

	1;
}

sub cb_delete_folder {
	my $self = shift;
	
	my $folder_object = $self->popup_folder_object;
	
	my $trash_id = $self->config('trash_folder_id');
	my $trash_object = JaM::Folder->by_id($trash_id);

	# check if this *is* the trash folder itself
	if ( $trash_id == $folder_object->id ) {
		$self->message_window (
			 message => "You can't trash trash."
		);
		return 1;
	}
	
	# check if this folder is already in trash
	my $trash_path = $trash_object->path;
	my $path = $folder_object->path;

	if ( $path =~ m!^$trash_path! ) {
		$self->message_window (
			message => "Folder is already in trash."
		);
		return 1;
	}

	# check if the trash folder is a descendant of this folder
	my $desc = $folder_object->descendants;
	if ( defined $desc->{$trash_id} ) {
		$self->message_window (
			message => "The trash folder is a descendant of this folder."
		);
		return 1;
	}

	# check if this folder is undeletable
	if ( $folder_object->undeletable ) {
		$self->message_window (
			message => "You can't delete this folder."
		);
		return 1;
	}

	# update database
	$self->move_tree (
		moved_object => $folder_object,
		parent_object => $trash_object,
	);
	
	# update gui: remove item
	my $folder_item = $self->gtk_folder_items->{$folder_object->id};
	$self->gtk_folders_tree->remove ($folder_item);
	
	# update gui: insert into trash
	$self->insert_folder_item (
		folder_object => $folder_object,
		sibling_item => undef,
	);

	1;
}

sub build_menu_of_folders {
	my $self= shift;
	my %par = @_;
	my ($callback) = @par{'callback'};

	my $root_folder = JaM::Folder->by_id(1);

	my $menu = $self->build_submenu (
		parent   => $root_folder,
		callback => $callback,
	);

#	my $menu = Gtk::Menu->new;
#	$menu->append($submenu);
#	$menu->show;
	
	return $menu;
}

sub build_submenu {
	my $self = shift;
	my %par = @_;
	my ($parent, $callback) = @par{'parent','callback'};
	
	my $menu;
	
	if ( not $parent->leaf ) {
		my $childs = JaM::Folder->query (
			where => "parent_id=?",
			params => [ $parent->id ]
		);

		$menu = Gtk::Menu->new;
		$menu->show;

		if ( $parent->id != 1 ) {
			my $drop_here = Gtk::MenuItem->new ("[Drop here]");
			$drop_here->signal_connect ("activate", sub { &$callback($parent->id) } );
			$drop_here->show;
			$menu->append($drop_here);
		}

		foreach my $folder ( sort { $a->path cmp $b->path} values %{$childs} ) {
			my $item = Gtk::MenuItem->new ($folder->name);
			$item->show;
			if ( $folder->leaf ) {
				$item->signal_connect ("activate", sub { &$callback($folder->id) } );
			}
			$menu->append($item);
	
			my $submenu = $self->build_submenu (
				parent => $folder,
				callback => $callback,
			);
			
			$item->set_submenu($submenu) if $submenu;
		}

	}
	
	return $menu;
}

sub cb_add_input_filter {
	my $self = shift;
	
	my $filter;
	eval { $filter = $self->comp('input_filter') };

	if ( not $filter ) {
	  	require JaM::GUI::IO_Filter;
	  	$filter = JaM::GUI::IO_Filter->new (
			dbh => $self->dbh,
		);
		$filter->open_window;
	}
	
	$filter->add_new_filter (
		folder_object => $self->popup_folder_object
	);
	
	$filter->gtk_win->focus(1);
	
	1;
}

sub empty_trash_folder {
	my $self = shift;
	
	my $trash_folder_id = $self->config('trash_folder_id');
	
	my $folder = JaM::Folder->by_id($trash_folder_id);

	my $folder_item = $self->gtk_folder_items->{$trash_folder_id};
	my $sibling_id = $folder->sibling_id;

	my $sibling_item;
	if ( $sibling_id ) {
		$sibling_item = $self->gtk_folder_items->{$sibling_id};
	}

	$folder->delete_content;

	$self->gtk_folders_tree->remove($folder_item);

	$self->insert_folder_item (
		folder_object => $folder,
		sibling_item  => $sibling_item,
	);

	$self->comp('folders')->update_folder_item (
		folder_object => $folder
	);
	
	1;
}

sub cb_ignore_reply_to {
	my $self = shift;
	
	my $folder_object = $self->popup_folder_object;
	
	$folder_object->ignore_reply_to (1);
	$folder_object->save;
	
	1;
}

sub cb_dont_ignore_reply_to {
	my $self = shift;
	
	my $folder_object = $self->popup_folder_object;
	
	$folder_object->ignore_reply_to (0);
	$folder_object->save;
	
	1;
}

1;