package GCMoviesLists;

###################################################
#
#  Copyright 2005 Tian
#
#  This file is part of GCfilms.
#
#  GCfilms is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  GCfilms is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with GCfilms; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
###################################################

use strict;

{
    package GCTextList;
    
    use Gtk2::SimpleList;
    use base "Gtk2::ScrolledWindow";

    sub new
    {
        my ($proto, $parent, $title) = @_;
        my $class = ref($proto) || $proto;
        my $self  = $class->SUPER::new;
        bless ($self, $class);

        $self->{parent} = $parent;
        
        $self->set_policy ('automatic', 'automatic');
        $self->set_shadow_type('etched-in');
        
        $self->{list} = new Gtk2::SimpleList($title=>'text');
        $self->{list}->set_name('GCMoviesTextList');
        $self->{list}->set_headers_clickable(1);
        $self->{list}->set_rules_hint(1);
        $self->{list}->get_column(0)->set_sort_indicator(1);
        
        $self->{list}->signal_connect (cursor_changed => sub {
            my ($sl, $path, $column) = @_;
            my @idx = $sl->get_selected_indices;
            $parent->display($idx[0]);
            $self->{currentIdx} = $idx[0];
        });

        $self->{list}->signal_connect ('row-activated' => sub {
           $parent->displayInWindow;
        });

        $self->add($self->{list});
        
        $self->{list}->signal_connect('button_press_event' => sub {
                my ($widget, $event) = @_;
                return 0 if $event->button ne 3;
                $self->{context}->popup(undef, undef, undef, undef, $event->button, $event->time);
                return 0;
        });
        
        $self->{list}->signal_connect('key-press-event' => sub {
                my ($widget, $event) = @_;
                my $key = Gtk2::Gdk->keyval_name($event->keyval);
                if ($key eq 'Delete')
                {
                    $self->{parent}->deleteCurrentMovie;
                    return 1;
                }
                return 0;
        });
        
        $self->{list}->get_column(0)->signal_connect('clicked' => sub {
            $parent->{movies}->changeOrder;
            $self->setSortOrder;
        });

        $self->{currentIdx} = 0;

        return $self;
    }

    sub setSortOrder
    {
        my $self = shift;
        my ($order, $field) = $self->{parent}->{movies}->getSortOrder;
        $self->{list}->get_column(0)->set_sort_order($order ? 'ascending' : 'descending');
    }

    sub clearCache
    {
        my $self = shift;
    }

    sub reset
    {
        my $self = shift;
        
        @{$self->{list}->{data}} = ();
    }
    
    sub done
    {
        my $self = shift;
    }
    
    sub addMovie
    {
        my ($self, $info) = @_;
        
        push @{$self->{list}->{data}}, $self->{parent}->transformTitle($info->{title});
    }
    
    sub removeMovie
    {
        my ($self, $number) = @_;
        
        splice @{$self->{list}->{data}}, $number, 1;
        
        #Reload not needed
        return 0;
    }
    
    sub select
    {
        my ($self, $idx, $id, $init) = @_;
        
        $self->{list}->select($idx);
        $self->{currentIdx} = $idx;
        return $idx;
    }
    
    sub showCurrent
    {
        my $self = shift;
        
   		my $path = $self->{list}->get_selection->get_selected_rows;
		$self->{list}->scroll_to_cell($path) if $path;
    }
    
    sub changeCurrent
    {
        #my ($self, $image, $borrower) = @_;
        my ($self, $previous, $new) = @_;

        $self->{list}->{data}->[$self->{currentIdx}] = $self->{parent}->transformTitle($new->{title});

        #No reload needed
        return 0;
    }
    
}

{
    package GCImageList;
    
    use File::Basename;
    use GCUtils;
    use GCStyle;
    use base "Gtk2::VBox";
    use File::Temp qw/ tempfile /;
    
    sub new
    {
        my ($proto, $parent, $title, $columns, $withImage) = @_;
        my $class = ref($proto) || $proto;
        my $self  = $class->SUPER::new;
        bless ($self, $class);
        
        $self->{withImage} = $parent->{options}->listBgPicture;
        
        $parent->{options}->listImgSkin($GCStyle::defaultList) if ! $parent->{options}->exists('listImgSkin');
        $self->{skin} = $parent->{options}->listImgSkin;

        $parent->{options}->listImgSize(2) if ! $parent->{options}->exists('listImgSize');
        $self->{size} = $parent->{options}->listImgSize;

        #Default value (also for size = 2)
        ($self->{imgWidth}, $self->{imgHeight}) = (120, 160);
        $self->{vboxWidth} = $self->{imgWidth} + 10;
        $self->{vboxHeight} = $self->{imgHeight} + 10;
        $self->{vboxHeight} += 20 if $self->{withImage};

        $self->{factor} = 1;

        if ($self->{size} == 0)
        {
            $self->{factor} = 0.5;
        }
        elsif ($self->{size} == 1)
        {
             $self->{factor} = 0.8;
        }
        elsif ($self->{size} == 3)  
        {
            $self->{factor} = 1.5;
        }
        elsif($self->{size} == 4)
        {
            $self->{factor} = 2;
        }
        
        $self->{imgWidth} *= $self->{factor};
        $self->{imgHeight} *= $self->{factor};
        $self->{vboxWidth} = $self->{imgWidth} + (10 * $self->{factor});
        $self->{vboxHeight} = $self->{imgHeight} + (10 * $self->{factor});
        $self->{vboxHeight} += (20 * $self->{factor}) if $self->{withImage};
        
        $self->{scroll} = new Gtk2::ScrolledWindow;
        $self->{scroll}->set_policy ('automatic', 'automatic');
        $self->{scroll}->set_shadow_type('etched-in');
        
        $self->{parent} = $parent;
        
        $self->{sortButton} = Gtk2::Button->new_from_stock('gtk-sort-ascending');
        
        #$self->{list} = new Gtk2::Table(0,$columns,1);
        $self->{list} = new Gtk2::VBox(0,0);
        $self->{tooltips} = Gtk2::Tooltips->new();
        
        $self->{columns} = $columns;
        $self->reset;
        $self->clearCache;

        $self->{lendPicture} = $parent->{style}->{dir}.'/lend.png';
        $self->{lendPixbuf} = Gtk2::Gdk::Pixbuf->new_from_file($self->{lendPicture});
        $self->{lendPixbuf} = GCUtils::scaleMaxPixbuf($self->{lendPixbuf},
                                                      $self->{imgWidth},
                                                      $self->{imgHeight},
                                                      1);
        
        $self->{defaultImage} = $parent->{defaultImage};
        
        #$self->{list}->set_row_spacings(0);
        #$self->{list}->set_col_spacings(0);
        $self->{list}->set_border_width(0);
        $self->{list}->set_border_width(5) if ! $self->{withImage};
        
        $self->{scroll}->add_with_viewport($self->{list});
    
        if ($self->{withImage})
        {
            my $bgdir = $ENV{GCF_SHARE_DIR}.'/list_bg/'.$self->{skin};

            $self->{selectedPixmap} = $bgdir.'/list_bg_selected.png';
            $self->{unselectedPixmap} = $bgdir.'/list_bg_unselected.png';
            $self->{bgPixmap} = $bgdir.'/list_bg.png';

            my $tmpPixbuf = Gtk2::Gdk::Pixbuf->new_from_file($self->{bgPixmap});
            $tmpPixbuf = GCUtils::scaleMaxPixbuf($tmpPixbuf,
                                                 $self->{vboxWidth},
                                                 $self->{vboxHeight},
                                                 1);
            (my $fh, $self->{tmpBgPixmap}) = tempfile;
            close $fh;
            $tmpPixbuf->save($self->{tmpBgPixmap}, 'png');
            GCUtils::setWidgetPixmap($self->{list}->parent, $self->{tmpBgPixmap});
            
            $self->{backgroundPixbuf} = Gtk2::Gdk::Pixbuf->new_from_file($self->{unselectedPixmap});
            $self->{backgroundPixbuf} = GCUtils::scaleMaxPixbuf($self->{backgroundPixbuf},
                                                                $self->{vboxWidth},
                                                                $self->{vboxHeight},
                                                                1);
            $self->{selectedPixbuf} = Gtk2::Gdk::Pixbuf->new_from_file($self->{selectedPixmap});
            $self->{selectedPixbuf} = GCUtils::scaleMaxPixbuf($self->{selectedPixbuf},
                                                              $self->{vboxWidth},
                                                              $self->{vboxHeight},
                                                              1);
        }
        else
        {
            my @colors = split m/,/, $parent->{options}->listBgColor;
            ($colors[0], $colors[1], $colors[2]) = (65535, 65535, 65535) if !@colors;
            $self->{inactiveBg} = new Gtk2::Gdk::Color($colors[0], $colors[1], $colors[2]);
            @colors = split m/,/, $parent->{options}->listFgColor;
            ($colors[0], $colors[1], $colors[2]) = (0, 0, 0) if !@colors;
            $self->{activeBg} = new Gtk2::Gdk::Color($colors[0], $colors[1], $colors[2]);

            $self->{list}->parent->modify_bg('normal', $self->{inactiveBg});
            $self->{list}->parent->modify_bg('active', $self->{inactiveBg});
            $self->{list}->parent->modify_bg('prelight', $self->{inactiveBg});
            $self->{list}->parent->modify_bg('selected', $self->{inactiveBg});
            $self->{list}->parent->modify_bg('insensitive', $self->{inactiveBg});
        }

        $self->pack_start($self->{sortButton},0,0,0);
        $self->pack_start($self->{scroll},1,1,0);
        
        $self->{list}->can_focus(1);
        
        $self->{sortButton}->signal_connect('clicked' => sub {
            $parent->{movies}->changeOrder;
            $self->setSortOrder;
        });
        
        
        return $self;
    }

    sub DESTROY
    {
        my $self = shift;
        
        unlink $self->{tmpBgPixmap};
        $self->SUPER::DESTROY;
    }

    sub setSortOrder
    {
        my $self = shift;
        my ($order, $field) = $self->{parent}->{movies}->getSortOrder;
        
        $self->remove($self->{sortButton});
        $self->{sortButton}->destroy;
        $self->{sortButton} = Gtk2::Button->new_from_stock($order ? 'gtk-sort-ascending' : 'gtk-sort-descending');
        $self->pack_start($self->{sortButton},0,0,0);
        $self->reorder_child($self->{sortButton},0);
        $self->{sortButton}->show_all;
        
        $self->{sortButton}->signal_connect('clicked' => sub {
            $self->{parent}->{movies}->changeOrder;
            $self->setSortOrder;
        });
        
    }

    sub clearCache
    {
        my $self = shift;
        
        if ($self->{cache})
        {
            foreach (values %{$self->{cache}})
            {
                $_->{eventBox}->destroy;
            }
        }
        
        $self->{cache} = {};
    }

    sub reset
    {
        my $self = shift;
        
        #Restore current picture if modified
        $self->{boxes}->[$self->{current}]->child->set_from_pixbuf($self->{previousPixbuf})
            if ($self->{previousPixbuf} && $self->{boxes}->[$self->{current}]->child);
        
        $self->{boxes}->[$self->{current}]->modify_bg('normal', $self->{inactiveBg})
            if (! $self->{withImage}) && $self->{boxes}->[$self->{current}];
        
        my @array = ();
        $self->{boxes} = \@array;
        $self->{number} = 0;
        $self->{current} = 0;
        $self->{previous} = 0;
        $self->{previousPixbuf} = undef;
        $self->{list}->hide;
        my @children = $self->{list}->get_children;
        foreach (@children)
        {
            my @children2 = $_->get_children;
            foreach my $child(@children2)
            {
                $_->remove($child);
            }
            $self->{list}->remove($_);
        }
        $self->{enhanceInformation} = [];
        
        Glib::Timeout->add(200, sub {
            $self->{list}->show_all;
        });
        
        $self->{initializing} = 1;
    }
    
    sub done
    {
        my $self = shift;
        
        my $i = 1;
        
        $self->{list}->show_all;
        $self->{initializing} = 0;
        foreach (@{$self->{enhanceInformation}})
        {
            Glib::Timeout->add($i * 30,
                              \&enhancePicture,
                              $_);
                              
            $i++;
        }
    }
    
    sub createPixbuf
    {
        my ($self, $displayedImage, $borrower) = @_;
    
        my $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($displayedImage);
        $pixbuf = GCUtils::scaleMaxPixbuf($pixbuf,
                                          $self->{imgWidth},
                                          $self->{imgHeight},
                                          $self->{withImage},
                                          $self->{initializing});

        if ($borrower && ($borrower ne 'none'))
        {
           $self->{lendPixbuf}->composite($pixbuf,
                                          0, 0, 
                                          $pixbuf->get_width, $pixbuf->get_height,
                                          0, 0,
                                          1, 1, 
                                          'nearest', 255);
        }
        
        if ($self->{withImage})
        {
            my $offsetX = 5 * $self->{factor};
            my $offsetY = 15 * $self->{factor};
            my $bgPixbuf = $self->{backgroundPixbuf}->copy;
            $pixbuf->composite($bgPixbuf,
                                  $offsetX, $offsetY, 
                                  $self->{imgWidth}, $self->{imgHeight},
                                  $offsetX, $offsetY,
                                  1, 1, 
                                  'nearest', 255);
            $pixbuf = $bgPixbuf;
        }
        return $pixbuf;
    }
    
    sub enhancePicture
    {
        my $data = shift;
        my $eventBox = $data->[1];
        return if ! $eventBox->child;
        my $self = $data->[0];
        my $pixbuf = $self->createPixbuf($data->[2], $data->[3]);
        $eventBox->child->set_from_pixbuf($pixbuf);

        if ($self->{boxes}->[$self->{current}] == $eventBox)
        {
            $self->{previousPixbuf} = $pixbuf;
            $self->select($self->{current});
        }
        
        return 0;
    }
    
    sub createEventBox
    {
        my ($self, $info) = @_;
    
        my $eventBox = new Gtk2::EventBox;
        $eventBox->can_focus(1);
                
        my $image = new Gtk2::Image;
        my $displayedImage = GCUtils::getDisplayedImage($info->{image},
                                                        $self->{defaultImage},
                                                        $self->{parent}->{options}->file);

        my $pixbuf = $self->createPixbuf($displayedImage, $info->{borrower});

        $self->{tooltips}->set_tip($eventBox, $self->{parent}->transformTitle($info->{title}), '');

        if (! $self->{withImage})
        {
            $eventBox->modify_bg('normal', $self->{inactiveBg});
        }

        $image->set_from_pixbuf($pixbuf);
        
        $eventBox->add($image);
        $eventBox->set_size_request($self->{vboxWidth},$self->{vboxHeight});

        if ($self->{initializing})
        {
            push @{$self->{enhanceInformation}}, [$self, $eventBox, $displayedImage, $info->{borrower}];
        }
        else
        {
            $eventBox->show_all;
        }
        
        return $eventBox;
    }

    sub getFromCache
    {
        my ($self, $info) = @_;
        
        if (($info->{id} == '') || 
            (! $self->{cache}->{$info->{id}}))
        {
            my $item = {};
            $item->{eventBox} = $self->createEventBox($info);
            $item->{keyHandler} = 0;
            $item->{mouseHandler} = 0;
        
            $self->{cache}->{$info->{id}} = $item;
        }
        return $self->{cache}->{$info->{id}};
    }
    
    sub addMovie
    {
        my ($self, $info) = @_;
        
        my $item = $self->getFromCache($info);
        my $eventBox = $item->{eventBox};
        my $i = $self->{number};
        
        $eventBox->signal_handler_disconnect($item->{mouseHandler})
            if $item->{mouseHandler};
        $item->{mouseHandler} = $eventBox->signal_connect('button_press_event' => sub {
                my ($widget, $event) = @_;
                $self->{parent}->display($i) unless $event->type eq '2button-press';
                $self->select($i);
                $self->{parent}->displayInWindow if $event->type eq '2button-press';
                $self->{context}->popup(undef, undef, undef, undef, $event->button, $event->time)
                    if $event->button eq 3;
                $widget->grab_focus;
            });

        $eventBox->signal_handler_disconnect($item->{keyHandler})
            if $item->{keyHandler};

        $item->{keyHandler} = $eventBox->signal_connect('key-press-event' => sub {
                my ($widget, $event) = @_;
                my $idx = $i;
                my $key = Gtk2::Gdk->keyval_name($event->keyval);
                if ($key eq 'Delete')
                {
                    $self->{parent}->deleteCurrentMovie;
                    return 1;
                }
                if (($key eq 'Return') || ($key eq 'space'))
                {
                    $self->{parent}->displayInWindow;
                    return 1;
                }
                $idx++ if $key eq 'Right';
                $idx-- if $key eq 'Left';
                $idx += $self->{columns} if $key eq 'Down';
                $idx -= $self->{columns} if $key eq 'Up';
                return 1 if ($idx < 0) || ($idx >= scalar @{$self->{boxes}});
                my $column = $idx % $self->{columns};
                $self->select($idx);
                $self->{parent}->display($idx);
                $self->{boxes}->[$idx]->grab_focus;
                $self->showCurrent if $key eq 'Down'
                                   || $key eq 'Up'
                                   || (($key eq 'Left') && ($column == ($self->{columns} - 1)))
                                   || (($key eq 'Right') && ($column == 0));
                return 1;
                
            });

        if (($self->{number} % $self->{columns}) == 0)
        {
            #New row begin
            $self->{currentRow} = new Gtk2::HBox(0,0);
            $self->{list}->pack_start($self->{currentRow},0,0,0);
            $self->{currentRow}->show_all if ! $self->{initializing};
        }
        $self->{currentRow}->pack_start($eventBox,0,0,0);

        push @{$self->{boxes}}, $eventBox;
        
        $self->{number}++;
    }
    
    sub removeMovie
    {
        my ($self, $number, $id) = @_;

        $self->{cache}->{$id}->{eventBox}->destroy;
        delete $self->{cache}->{$id};

        #Reload needed
        return 1;
    }
        
    sub select
    {
        my ($self, $idx, $id, $init) = @_;
        
        my @boxes = @{$self->{boxes}};
        
        return if ! scalar(@boxes);
        
        my $previous = $self->{current};
        $self->{current} = $idx;
        
        if ($self->{withImage})
        {
            GCUtils::setWidgetPixmap($boxes[$previous], $self->{unselectedPixmap});
            GCUtils::setWidgetPixmap($boxes[$self->{current}], $self->{selectedPixmap});
        }
        else
        {
            $boxes[$previous]->modify_bg('normal', $self->{inactiveBg});  
            $boxes[$self->{current}]->modify_bg('normal', $self->{activeBg});
        
        }
        $boxes[$previous]->child->set_from_pixbuf($self->{previousPixbuf})
            if $self->{previousPixbuf} && $boxes[$previous]->child;
        my $pixbuf = $boxes[$self->{current}]->child->get_pixbuf;
        $self->{previousPixbuf} = $pixbuf->copy;
        
        $pixbuf->saturate_and_pixelate($pixbuf, 1.5, 0);

        $self->{selectedPixbuf}->composite($pixbuf,
                           0, 0, 
                           $self->{vboxWidth}, $self->{vboxHeight},
                           0, 0,
                           1, 1, 
                           'nearest', 255)
            if $self->{withImage};
        
            
        $boxes[$self->{current}]->child->set_from_pixbuf($pixbuf);
        
        $self->{previous} = $previous;

        return $idx;        
    }
    
    sub showCurrent
    {
        my $self = shift;
 
        if ($self->{initializing})
        {
            Glib::Timeout->add(100 ,\&showCurrent, $self);
            return;
        }
 
        my $adj = $self->{scroll}->get_vadjustment;
        my $totalRows = int $self->{number} / $self->{columns};
        my $row = (int $self->{current} / $self->{columns}) - 1;

        $adj->set_value((($adj->upper - $adj->lower) / $totalRows) * $row) if $totalRows > 0;
    }

    sub changeCurrent
    {
        #my ($self, $image, $borrower) = @_;
        my ($self, $previous, $new) = @_;
        #To ease comparison, do some modifications.
        #empty borrower is equivalent to 'none'.

        $previous->{borrower} = '' if $previous->{borrower} eq 'none';
        $new->{borrower} = '' if $new->{borrower} eq 'none';
        $self->{tooltips}->set_tip($self->{boxes}->[$self->{current}], $self->{parent}->transformTitle($new->{title}), '')
            if ($new->{title} ne $previous->{title});
        return 0 if ($previous->{image} eq $new->{image}) && ($previous->{borrower} eq $new->{borrower});
        my ($image, $borrower) = ($new->{image}, $new->{borrower});

        my @boxes = @{$self->{boxes}};
        
        my $displayedImage = GCUtils::getDisplayedImage($image,
                                                        $self->{defaultImage},
                                                        $self->{parent}->{options}->file);
        
        my $pixbuf = $self->createPixbuf($displayedImage, $borrower);

        $self->{previousPixbuf} = $pixbuf->copy;
        $pixbuf->saturate_and_pixelate($pixbuf, 1.5, 0);
        $self->{selectedPixbuf}->composite($pixbuf,
                                           0, 0, 
                                           $self->{vboxWidth}, $self->{vboxHeight},
                                           0, 0,
                                           1, 1, 
                                           'nearest', 255)
            if $self->{withImage};
        $boxes[$self->{current}]->child->set_from_pixbuf($pixbuf);

        #No reload needed
        return 0;
    }    
}

{
    package GCDetailedList;
    
    use File::Basename;
    use base "Gtk2::ScrolledWindow";

    sub new
    {
        my ($proto, $parent, $title) = @_;
        my $class = ref($proto) || $proto;
        my $self  = $class->SUPER::new;
        bless ($self, $class);

        $self->{parent} = $parent;
        
        $parent->{options}->groupMovies(1) if ! $parent->{options}->exists('groupMovies');
        $self->{groupMovies} = $parent->{options}->groupMovies;
        
        $self->{imgWidth} = 60;
        $self->{imgHeight} = 80;
        
        $parent->{options}->listImgSize(2) if ! $parent->{options}->exists('listImgSize');
        my $size = $parent->{options}->listImgSize;
        $self->{factor} = 1;

        if ($size == 0)
        {
            $self->{factor} = 0.5;
        }
        elsif ($size == 1)
        {
             $self->{factor} = 0.8;
        }
        elsif ($size == 3)  
        {
            $self->{factor} = 1.5;
        }
        elsif($size == 4)
        {
            $self->{factor} = 2;
        }
        
        $self->{imgWidth} *= $self->{factor};
        $self->{imgHeight} *= $self->{factor};
        
        $self->{defaultImage} = $parent->{defaultImage};

        $self->clearCache;
        
        $self->set_policy ('automatic', 'automatic');
        $self->set_shadow_type('etched-in');
        
        $parent->{options}->details('image|title|date|director|type')
            if ! $parent->{options}->exists('details');
        my @tmpArray = split m/\|/, $parent->{options}->details;
        $self->{fieldsArray} = \@tmpArray;
        
        $self->{titleIndex} = -1;
        $self->{imageIndex} = -1;
        my @columnsType;
        $self->{columnsArray} = [];
        $self->{columns} = {};
        my $col = 0;
        $self->{rankIndex} = -1;
        $self->{collectionIndex} = -1;
        $self->{addRank} = 0;
        $self->{addCollection} = 0;
        foreach my $field(@tmpArray)
        {
            my $title = $parent->{lang}->{Displayed}->{$field};
        
            my $renderer;
            my $attribute;
            if ($field eq 'image')
            {
                push @columnsType, 'Gtk2::Gdk::Pixbuf';
                $renderer = Gtk2::CellRendererPixbuf->new;
                $attribute = 'pixbuf';
                $self->{imageIndex} = $col;
            }
            elsif ($field eq 'rank')
            {
                push @columnsType, 'Glib::Int';
                $renderer = Gtk2::CellRendererText->new;
                $attribute = 'text';
                $self->{rankIndex} = $col;
            }
            else
            {
               push @columnsType, 'Glib::String';
               $renderer = Gtk2::CellRendererText->new;
               $attribute = 'text';
               $self->{titleIndex} = $col if ($field eq 'title');
               $self->{collectionIndex} = $col if ($field eq 'collection');
            }
            $self->{columns}->{$field} = Gtk2::TreeViewColumn->new_with_attributes($title, $renderer, $attribute => $col);
            $self->{columns}->{$field}->set_resizable(1);
            #$self->{columns}->{$field}->set_reorderable(1);
            if ($field eq 'image')
            {
                $self->{columns}->{$field}->set_clickable (0);
            }
            else
            {
                $self->{columns}->{$field}->signal_connect('clicked' => sub {
                    $parent->{movies}->changeOrder($field);
                    $self->setSortOrder;
                });
            }
            push @{$self->{columnsArray}}, $self->{columns}->{$field};
            $col++;
        }
        push @columnsType, 'Glib::Int';
        $self->{idxColumn} = $col;
        if ($self->{rankIndex} == -1)
        {
            push @columnsType, 'Glib::Int';
            $self->{addRank} = 1;
            $col++;
            $self->{rankIndex} = $col;
        }
        if ($self->{collectionIndex} == -1)
        {
            push @columnsType, 'Glib::String';
            $self->{addCollection} = 1;
            $col++;
            $self->{collectionIndex} = $col;
        }
        $self->{model} = new Gtk2::TreeStore(@columnsType);
        
        my $flags = $self->{model}->get_flags;
        
        $self->{list} = Gtk2::TreeView->new_with_model($self->{model});
        $self->{list}->append_column($_) foreach (@{$self->{columnsArray}});
        $self->{list}->set_name('GCMoviesDetailsList');
        $self->{list}->set_headers_clickable(1);
        $self->{list}->set_rules_hint(1);
        $self->{list}->set_reorderable(1);
                
        #$self->{list}->get_selection->set_mode ('multiple');
        
        $self->{list}->signal_connect (cursor_changed => sub {
            my ($sl, $path, $column) = @_;
            my $iter = $sl->get_selection->get_selected;
            $parent->display($self->convertIterToIdx($iter));
            $self->{currentIterString} = $self->convertIterToString($iter);
        });

        $self->{list}->signal_connect ('row-activated' => sub {
           $parent->displayInWindow;
        });

        $self->add($self->{list});
        
        $self->{list}->signal_connect('button_press_event' => sub {
                my ($widget, $event) = @_;
                return 0 if $event->button ne 3;
                $self->{context}->popup(undef, undef, undef, undef, $event->button, $event->time);
                return 0;
        });

        $self->{list}->signal_connect('key-press-event' => sub {
                my ($widget, $event) = @_;
                my $key = Gtk2::Gdk->keyval_name($event->keyval);
                if ($key eq 'Delete')
                {
                    $self->{parent}->deleteCurrentMovie;
                    return 1;
                }
                return 0;
        });

        if ($self->{groupMovies})
        {
            my $targetEntryMove = {
                target => 'MY_ROW_TARGET',
                flags => ['same-widget'],
                info => 42,
            };
            
            $self->{list}->enable_model_drag_source('button1-mask','move', $targetEntryMove);
            $self->{list}->enable_model_drag_dest('move', $targetEntryMove);
    
            $self->{list}->signal_connect('drag_data_get' => sub {
                return 1;
            });
    
            $self->{list}->signal_connect('drag_data_received' => \&dropHandler, $self);
        }
        else
        {
            $self->{list}->unset_rows_drag_dest;
            $self->{list}->unset_rows_drag_source;
        }

        $self->reset;
        
        return $self;
    }

    sub dropHandler
    {
        my ($treeview, $context, $widget_x, $widget_y, $data, $info,$time, $self) = @_;
        my $source = $context->get_source_widget;
        return if ($source ne $treeview);
        my $model = $treeview->get_model;
        my ($targetPath, $targetPos) = $treeview->get_dest_row_at_pos($widget_x, $widget_y);
        if ($targetPath)
        {
            my $targetIter = $model->get_iter($targetPath);
            my $targetIdx = $self->convertIterToIdx($targetIter);
            my $origIter = $treeview->get_selection->get_selected;
            my $origIdx = $self->convertIterToIdx($origIter);
            #We cannot drop an item on itself
            if ($targetIter == $origIter)
            {
                $context->finish(1,0,$time);
                return;
            }
            my @origData;
            my $i = 0;
            foreach ($model->get_value($origIter))
            {
                push @origData, $i, $_;
                $i++;
            }
            if ($targetPos =~ /^into/)
            {
                if (($targetPath->get_depth > 1) || $model->iter_has_child($origIter))
                {
                    #We can't add an item to a collection item.
                    $context->finish(1,0,$time);
                }
                else
                {
                    #Creating a new collection item
                    my $newIter = $model->append($targetIter);
                    my $collection = $self->{parent}->{movies}->getValue($targetIdx, 'collection');
                    #We use parent title as collection if there is none.
                    if (!$collection)
                    {
                        $collection = $self->{parent}->{movies}->getValue($targetIdx, 'title') if !$collection;
                        $self->{parent}->{movies}->setValue($targetIdx, 'collection', $collection);
                    }
                    $model->set($newIter,@origData);
                    $self->{parent}->{movies}->setValue($origIdx, 'collection', $collection);
                    $self->updateRank($newIter, $self->{parent}->{movies}->getMaxRank($collection) + 1);
                    $context->finish(1,1,$time);
                }
            }
            else
            {
                my $origPath = $model->get_path($origIter);
                if ($targetPath->get_depth == 1)
                {
                    if  ($origPath->get_depth == 1)
                    {
                        #Just moving a master item is not allowed
                        $context->finish(1,0,$time);
                    }
                    else
                    {
                        #We get an item out of a collection
                        #We change its values and requires a reload to put it as needed by sort.
                        $self->{parent}->{movies}->setValue($origIdx, 'collection', '');
                        $self->updateRank($origIter, 0);
                        $self->{parent}->{movies}->reloadList;
                    }
                }
                else
                {
                    #We are placing a collection item
                    my $newIter;
                    my $newRank = $self->getIterRank($targetIter);
                    if ($targetPos eq 'before')
                    {
                        $newIter = $model->insert_before($model->iter_parent($targetIter),
                                                         $targetIter);
                    }
                    else
                    {
                        $newIter = $model->insert_after($model->iter_parent($targetIter),
                                                         $targetIter);
                        $newRank++;
                    }
                    my $collection = $self->{parent}->{movies}->getValue($targetIdx, 'collection');
                    $model->set($newIter,@origData);
                    $self->{parent}->{movies}->setValue($origIdx, 'collection', $collection);
                    $self->updateRank($newIter, $newRank);
                    $self->checkRanks($newIter);
                    $context->finish(1,1,$time);                    
                }
            }
            $self->select($origIdx);
            #$context->abort($time);
            #return 1;
        }
    }

    sub updateRank
    {
        my ($self, $iter, $rank) = @_;
        
        my $idx = $self->convertIterToIdx($iter);
        
        $self->{model}->set($iter, $self->{rankIndex}, $rank);
        $self->{parent}->{movies}->setValue($idx, 'rank', $rank);
    }

    sub checkRanks
    {
        my ($self, $iter) = @_;
        
        my $previousRank = $self->getIterRank($iter);
        my $updatedIdx = $self->convertIterToIdx($iter);
        while ($iter = $self->{model}->iter_next($iter))
        {
            my $currentIdx = $self->convertIterToIdx($iter);
            next if $currentIdx == $updatedIdx;
            my $currentRank = $self->getIterRank($iter);
            if ($currentRank <= $previousRank)
            {
                $self->updateRank($iter, $previousRank + 1);
            }
            $previousRank = $self->getIterRank($iter);
        }
    }

    sub setSortOrder
    {
        my $self = shift;
        my ($order, $field) = $self->{parent}->{movies}->getSortOrder;

        foreach (keys %{$self->{columns}})
        {
            if ($_ eq $field)
            {
                $self->{columns}->{$_}->set_sort_indicator(1);
                $self->{columns}->{$_}->set_sort_order($order ? 'ascending' : 'descending');
            }
            else
            {
                $self->{columns}->{$_}->set_sort_indicator(0);
            }
        }
        $self->{sortField} = $field;
    }

    sub getPixbufFromCache
    {
        my ($self, $path) = @_;
        
        my $realPath = GCUtils::getDisplayedImage($path,
                                                  $self->{defaultImage},
                                                  $self->{parent}->{options}->file);
        
        if (! $self->{cache}->{$realPath})
        {
            my $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($realPath);
            $pixbuf = GCUtils::scaleMaxPixbuf($pixbuf,
                                              $self->{imgWidth},
                                              $self->{imgHeight},
                                              $self->{withImage});
            $self->{cache}->{$realPath} = $pixbuf;
        }
        return $self->{cache}->{$realPath};
    }

    sub removePixbufFromCache
    {
        my ($self, $path) = @_;
        
        my $realPath = GCUtils::getDisplayedImage($path,
                                                  $self->{defaultImage},
                                                  $self->{parent}->{options}->file);
        
        if (exists $self->{cache}->{$realPath})
        {
            delete $self->{cache}->{$realPath};
        }
    }

    sub clearCache
    {
        my $self = shift;
        $self->{cache} = {};
    }

    sub reset
    {
        my $self = shift;
        $self->{list}->set_model(undef);
        $self->{model}->clear;
        $self->{alreadyInserted} = {};
        $self->{waiting} = {};
        $self->{currentIdx} = 0;
        $self->{nextMovieIdx} = -1;
    }

    sub done
    {
        my $self = shift;
        $self->{list}->set_model($self->{model});
    }

    sub transformValue
    {
        my ($self, $value, $field) = @_;
        
        my $newValue = $value;
        $newValue =~ s/;.*?(,|$)/$1/g if $field eq 'audio';
        $newValue = $self->{parent}->transformTitle($newValue) if $field eq 'title';
        $newValue = $self->{parent}->{lang}->{PanelNobody}
            if ($field eq 'borrower') && ((! $value) || ($value eq 'none'));
        if ($field eq 'image')
        {
            $newValue = $self->getPixbufFromCache($value);
        }
        else
        {
            $newValue =~ s/,*$//;
            $newValue =~ s/,([^ ])/, $1/g;
        }
        return $newValue;
    }

    sub getIterRank
    {
        my ($self, $iter) = @_;
        
        return ($self->{model}->get($iter))[$self->{rankIndex}];
    }

    sub getIterCollection
    {
        my ($self, $iter) = @_;
        return ($self->{model}->get($iter))[$self->{collectionIndex}];
    }
    
    sub convertIterToString
    {
        my ($self, $iter) = @_;
        return '' if ! $iter;

        return $self->{model}->get_string_from_iter($iter);
    }
    
    sub convertIterToIdx
    {
        my ($self, $iter) = @_;
        return 0 if ! $iter;
        
        return ($self->{model}->get($iter))[$self->{idxColumn}];
    }
    
    sub convertIdxToIter
    {
        my ($self, $idx) = @_;
        $self->{model}->foreach(sub {
            my ($model, $path, $iter) = @_;
            $self->{currentIterString} = $model->get_path($iter)->to_string;
            return 1 if ($model->get($iter))[$self->{idxColumn}] == $idx;
        });
    }
    
    sub getCurrentId
    {
        my $self = shift;
        
        return $self->convertIterToIdx($self->getCurrentIter);
    }
    
    sub getCurrentIter
    {
        my $self = shift;
        
        return $self->{model}->get_iter_first if ! $self->{currentIterString};
        return $self->{model}->get_iter_from_string($self->{currentIterString});
    }    

    sub insertAtPosition
    {
        my ($self, $master, $rank) = @_;
    
        my $nextIter = $self->{model}->iter_nth_child($master, 0);
        while ($nextIter)
        {
            last if $self->getIterRank($nextIter) > $rank;
            $nextIter = $self->{model}->iter_next($nextIter);
        }
        my $childIter;
        if ($nextIter)
        {
            $childIter = $self->{model}->insert_before($master, $nextIter);                 
        }
        else
        {
            $childIter = $self->{model}->append($master);
        }
        return $childIter;
    }

    sub findMaster
    {
        my ($self, $collection) = @_;
        my $master = $self->{model}->get_iter_first;
        
        while ($master)
        {
            last if $self->getIterCollection($master) eq $collection;
            $master = $self->{model}->iter_next($master);
        }
        return $master;
    }

    sub addMovie
    {
        my ($self, $info) = @_;

        $self->{nextMovieIdx}++;

        my $collection = $info->{collection};
        my $rank = $info->{rank};

        #Creating data;        
        my @data;
        my $col = 0;
        
        foreach my $field(@{$self->{fieldsArray}})
        {
            my $value = $self->transformValue($info->{$field}, $field);
            push @data, $col, $value;
            $col++;
        }
        push @data, $col, $self->{nextMovieIdx};
        $col++;
        if ($self->{addRank})
        {
            push @data, $col, $rank;
            $col++;
        }
        push @data, $col, $collection if $self->{addCollection};
                
        if ((!$collection) || (!$self->{groupMovies}))
        {
            #Simple entry
            my $iter = $self->{model}->append(undef);
            $self->{model}->set($iter, @data);
            return;
        }
        
        if (exists $self->{alreadyInserted}->{$collection})
        {
            #Master already exists
            my $master = $self->{model}->get_iter_from_string($self->{alreadyInserted}->{$collection});
            my $currentIterString = '';
            my $childIter = $self->insertAtPosition($master, $rank);
            $self->{model}->set($childIter, @data);
        }
        elsif ($rank > $self->{parent}->{movies}->getMinRank($collection))
        {
            #No master and we are a child;
            push @{$self->{waiting}->{$collection}->{$rank}}, [$self->{nextMovieIdx}, \@data];
        }
        else
        {
            #A new master
            my $iter = $self->{model}->append(undef);
            $self->{alreadyInserted}->{$collection} = $self->{model}->get_path($iter)->to_string;
            $self->{model}->set($iter, @data);
            foreach my $childRank(sort {$a <=> $b} keys %{$self->{waiting}->{$collection}})
            {
                foreach my $childInfo(@{$self->{waiting}->{$collection}->{$childRank}})
                {
                    my $childIter = $self->{model}->append($iter);
                    $self->{model}->set($childIter, @{$childInfo->[1]});
                }
            }
        }
    }
    
    sub removeMovie
    {
        my ($self, $number, $prevId, $info) = @_;
        
        $self->convertIdxToIter($number);

        $self->removePixbufFromCache($info->{image});
        
        #Do a reload in some situations
        return 1
            ## We have a collection 
            if ($info->{collection})
            &&
            # And it is the previous master
            ($info->{rank} <= $self->{parent}->{movies}->getMinRank($info->{collection}));
        
        #Shift backward all following movies.
        $self->{model}->foreach(sub {
            my ($model, $path, $iter) = @_;
            my $currentIdx = ($model->get($iter))[$self->{idxColumn}];
            if ($currentIdx > $number)
            {
                $model->set($iter, $self->{idxColumn}, $currentIdx - 1);
            }
        });
        
        my $iter = $self->getCurrentIter;
        $self->{model}->remove($iter);
        
        #Reload not needed
        return 0;
    }

    sub select
    {
        my ($self, $idx, $id, $init) = @_;
        $self->convertIdxToIter($idx);
        $self->{currentIterString} = '0' if ! $self->{currentIterString};
        my $currentIter = $self->getCurrentIter;
        #$self->{currentIter} = $self->{model}->get_iter_from_string($self->{currentIterString});
        return if !$currentIter;
        if ($init)
        {
            my $parent = $self->{model}->iter_parent($currentIter);
            if ($parent)
            {
                my $treePath = $self->{model}->get_path($parent);
                if (!$self->{list}->row_expanded($treePath))
                {
                    $self->{currentIterString} = $self->convertIterToString($parent);
                    $idx = $self->getCurrentId;
                }
            }
        }
        else
        {
            $self->{list}->expand_to_path(Gtk2::TreePath->new($self->{currentIterString}))
                if $self->{currentIterString} =~ /:/;
        }
        $self->{list}->get_selection->select_iter($currentIter) if ($currentIter);
        #$self->showCurrent;
        return $idx;
    }
    
    sub showCurrent
    {
        my $self = shift;
   		my $path = $self->{list}->get_selection->get_selected_rows;
		$self->{list}->scroll_to_cell($path) if $path;
    }
    
    sub changeCurrent
    {
        my ($self, $previous, $new) = @_;
        my $hasChanged = 0;
        my $reloadNeeded = 0;
        my @data = ();
        my $col = 0;
        
        my $currentIter = $self->getCurrentIter;
        
        foreach my $field(@{$self->{fieldsArray}})
        {
            my $newValue = $new->{$field};
            if ($newValue ne $previous->{$field})
            {
                $hasChanged = 1;
                $reloadNeeded = 1 if ($field eq $self->{sortField});
            }
            $newValue = $self->transformValue($newValue, $field);
            push @data, $col, $newValue;
            $col++;
        }
        push @data, $col, $self->getCurrentId;
        $col++;
        if ($self->{addRank})
        {
            push @data, $col, $new->{rank};
            $col++;
            $hasChanged = 1 if ($new->{rank} != $previous->{rank});
        }
        if ($self->{addCollection})
        {
            push @data, $col, $new->{collection};
            $col++;
            $hasChanged = 1 if ($new->{collection} !~ /^$previous->{collection}$/i);
        }
        return 0 if !$hasChanged;
    
        if ($self->{groupMovies})
        {
            if ($previous->{collection} && (!$new->{collection}))
            {
                #We get an item out of a collection. It needs to be sorted
                $reloadNeeded = 1;
            }
            elsif (
                   $new->{collection}
                   &&
                   (
                    (
                     ($self->{model}->get_path($currentIter)->get_depth == 1) &&
                     ($new->{rank} >= $self->{parent}->{movies}->getMinRank($new->{collection}))
                    )
                    ||
                    (
                     ($self->{model}->get_path($currentIter)->get_depth == 2) &&
                     ($new->{rank} == $self->{parent}->{movies}->getMinRank($new->{collection})) &&
                     ($new->{rank} != $previous->{rank})
                    )
                   )
                  )
            {
                #Master has been changed
                $reloadNeeded = 1;
            }
            elsif (($previous->{collection} ne $new->{collection})
                || ($previous->{rank} != $new->{rank}))
            {
                #An item is integrated or moved into a collection
                #First we find its master
                my $parent;
                if ($previous->{collection} eq $new->{collection})
                {
                    #Just moving
                    $parent = $self->{model}->iter_parent($currentIter);
                }
                else
                {   
                    #Changing collection
                    $parent = $self->findMaster($new->{collection});
                }
                if ($parent)
                {
                    #First we insert it at correct position
                    my $childIter = $self->insertAtPosition($parent, $new->{rank});
                    my $rowReference = new Gtk2::TreeRowReference($self->{model}, $self->get_path($childIter));
                    #We remove previous one
                    $self->{model}->remove($currentIter);
                    #And we initialize some vars
                    $self->{currentIterString} = $rowReference->get_path->to_string;
                }
            }
        }
        $self->{model}->set($currentIter, @data) if ! $reloadNeeded;
        
        return $reloadNeeded;
    }
    
}


1;