{
    package GCPlugins;
    
###################################################
#
#  Copyright 2005-2007 Tian
#
#  This file is part of GCstar.
#
#  GCstar 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.
#
#  GCstar 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 GCstar; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
###################################################

use strict;
use File::Basename;
use GCUtils 'glob';    
use GCDialogs;

use base 'Exporter';
our @EXPORT = qw(%pluginsMap %pluginsNameArrays);

our %pluginsMap;
our %pluginsNameArrays;

sub loadPlugins
{
    my $model = shift;
    return if $pluginsNameArrays{$model};
    my $dir = $ENV{GCS_LIB_DIR}.'/GCPlugins/'.$model;
    foreach (glob "$dir/*.pm")
    {
        my $plugin = basename($_, '.pm')."\n";
        next if $plugin =~ /Common/;
        eval "use GCPlugins::".$model."::$plugin";
        (my $class = $plugin) =~ s/^GC/GCPlugin/;
        my $obj;
        eval "\$obj = new GCPlugins::".$model."::$class";
        die "Fatal error with plugin $plugin : $@" if $@;
        $pluginsMap{$model}->{$obj->getName} = $obj;
    }
    my @names = sort keys %{$pluginsMap{$model}};
    $pluginsNameArrays{$model} = \@names;
    
}

sub loadAllPlugins
{
    foreach my $dir(glob $ENV{GCS_LIB_DIR}.'/GCPlugins/*')
    {
        next if $dir =~ /PluginsBase/;
        my $model = basename($dir);
        next if ($model !~ /^GC/) || ($model eq 'GCstar');
        loadPlugins($model);
    }
}
    
    {
        package GCPluginJob;
        
        use Storable qw(store_fd fd_retrieve);
        use IO::Handle;
        use GCPlugins::GCPluginsBase;
        
        sub new
        {
            my ($proto, $command, $data) = @_;
            my $class = ref($proto) || $proto;
            
            my $self  = {command => $command, data => $data};
            $command->autoflush(1);
            $data->autoflush(1);
            #GCPlugins::loadPlugins if ! $ENV{GCS_PROFILING};

            bless ($self, $class);
            return $self;
        }
        
        sub run
        {
            my $self = shift;
            my $command = $self->{command};
            while (1)
            {
                my $info = fd_retrieve($command);
                $self->beginSearch($info);
            }
        }
        
        sub quit
        {
            my $self = shift;
            close $self->{command};
            close $self->{data};
            wait;
            exit;
        }
        
        sub beginSearch
        {
            my ($self, $info) = @_;
            my $pid;
            GCPlugins::loadPlugins($info->{model}) if $info->{model};
            if ($pid = fork)
            {
                my $command = $self->{command};
                $command->autoflush(1);
                my $cmd = readline($command);
                chomp $cmd;
                kill 9,$pid if ($cmd eq 'STOP');
                $self->quit if $cmd eq 'EXIT';
                wait;
            }
            else
            {
                #$info could be a simple hash or the plugin itself depending on the phase
                if ($info->{type} eq 'load')
                {
                    # Here we create the plugin
                    my $plugin = $pluginsMap{$info->{model}}->{$info->{name}};
                    $plugin->setProxy($info->{proxy});
                    $plugin->setCookieJar($info->{cookieJar});
                    $plugin->{type} = $info->{type};
                    $plugin->{urlField} = $info->{urlField};
                    $plugin->{bigPics} = $info->{bigPics};

                    $self->{currentPlugin} = $plugin;

                    $plugin->{title} = $info->{query};
                    $plugin->{searchField} = $info->{field};
                    $plugin->{pass} = $info->{pass};
                    $plugin->{nextUrl} = $info->{nextUrl};

                    $plugin->load;
                    store_fd $plugin, $self->{data};
                }
                elsif ($info->{type} ne 'exit')
                {
                    # Here we re-use it
                    my $plugin = $info;
                    store_fd $plugin->getItemInfo, $self->{data};
                }
                exit;   
            }
        }
    }
 
    use Gtk2;
 
    {
        package GCPluginsDialog;
        
        use base 'GCModalDialog';
    
        sub show
        {
            my $self = shift;
            $self->{useThisSite}->set_active(0);
            $self->SUPER::show();
            $self->show_all;
            $self->{fieldsList}->hide if $self->{hideFieldsList};
            my $path = $self->{pluginsList}->get_selection->get_selected_rows;
            $self->{pluginsList}->scroll_to_cell($path) if $path;
            
            my $response = $self->run;
            $self->{plugin} = undef;
            if ($response eq 'ok')
            {
                my $pluginName = '';
                my $idx = ($self->{pluginsList}->get_selected_indices)[0];
                $pluginName = $self->{pluginsList}->{data}->[$idx]->[0];

                $self->{plugin} = $self->{model}->getPlugin($pluginName);

                if ($self->{useThisSite}->get_active)
                {
                   $self->{parent}->{model}->{preferences}->plugin($pluginName);
                   $self->{parent}->checkPlugin;
                }
            }
            $self->hide;
        }
        
        sub new
        {
            my ($proto, $parent) = @_;
            my $class = ref($proto) || $proto;
            my $self  = $class->SUPER::new($parent,
                                           $parent->{lang}->{PluginsTitle},
                                           'gtk-jump-to'
                                          );
    
                bless ($self, $class);
    
            $self->{parent} = $parent;

            $self->{titleGroup} = new GCGroup($parent->{lang}->{PluginsQuery});
            my $queryHbox = new Gtk2::HBox(0,0);
            $self->{query} = new GCShortText;
            $self->{query}->signal_connect(activate => sub {
                $self->response('ok');
            });

            $self->{fieldsList} = new GCMenuList;
            $queryHbox->pack_start($self->{query}, 1, 1, 0);
            $queryHbox->pack_start($self->{fieldsList}, 0, 0, $GCUtils::halfMargin);
            $self->{titleGroup}->addWidget($queryHbox);
            $self->vbox->pack_start($self->{titleGroup}, 0, 0, 0);
        
        
            my $pluginGroup = undef;
            my $pluginsFrame = new GCGroup($parent->{lang}->{PluginsFrame});
             
            $self->{pluginsList} = new Gtk2::SimpleList($parent->{lang}->{PluginsName} => "text",
                                                        $parent->{lang}->{PluginsLang} => "text",
                                                        $parent->{lang}->{PluginsSearchFields} => "text",
                                                        $parent->{lang}->{PluginsAuthor} => "text",);
            $self->{pluginsList}->set_border_width(5);
            $self->{pluginsList}->set_rules_hint(1);
            $self->{pluginsList}->get_column(0)->set_min_width(150);
            for my $i (0..2)
            {
                $self->{pluginsList}->get_column($i)->set_resizable(1);
            }
            $self->{pluginsList}->signal_connect(row_activated => sub {
                $self->response('ok');
            });

            $self->{currentPluginList} = undef;

            #$self->setModel;
    
            my $scrollPanelList = new Gtk2::ScrolledWindow;
            $scrollPanelList->set_border_width(5);
            $scrollPanelList->set_policy ('never', 'automatic');
            $scrollPanelList->set_shadow_type('etched-in');
            $scrollPanelList->add($self->{pluginsList});

            $self->{useThisSite} = Gtk2::CheckButton->new($parent->{lang}->{PluginsUseSite});
            
            my $pluginBox = new Gtk2::VBox(0,0);
            $pluginBox->pack_start($scrollPanelList, 1, 1, 0);
            $pluginBox->pack_start(Gtk2::VBox->new, 0, 0, $GCUtils::halfMargin);
            $pluginBox->pack_start($self->{useThisSite}, 0, 0, 0);
            
            $pluginsFrame->addWidget($pluginBox);
            $self->vbox->pack_start($pluginsFrame, 1, 1, 0);

            $self->set_default_size(1,550);
            
            return $self;
        }
    
        sub query
        {
            my $self = shift;
            if (@_)
            {
               my ($query, $field, $queries) = @_;
               $self->{fieldsList}->setValue($field);
               $self->{query}->setValue($query);
               $self->{queries} = $queries;
               $self->{fieldsList}->signal_connect('changed' => sub {
                   $self->{query}->setValue(
                       $self->{queries}->{
                           $self->{fieldsList}->getValue
                       }
                   );
               });
               #$self->{fieldsList}->setDefaultValues(shift);
               #$self->{searchField} = shift;
            }
            return ($self->{query}->getValue, $self->{fieldsList}->getValue);
        }
    
        sub plugin
        {
            my $self = shift;
            
            return $self->{plugin};
        }

        sub setModel
        {
            my ($self, $model, $list) = @_;
            $model ||= $self->{parent}->{model};
            my @plugins_array = sort split (",", $list) if $list;
    
            my @fields = @{$model->getSearchFields};
            if ($#fields < 1)
            {
                $self->{hideFieldsList} = 1;
            }
            else
            {
                $self->{hideFieldsList} = 0;
                my @values;
                foreach (@fields)
                {
                    push @values, {value => $_,
                                   displayed => $model->getDisplayedText($model->{fieldsInfo}->{$_}->{label})};
                }
                $self->{fieldsList}->setValues(\@values);
            }

#            if ($model)
#            {
#                my $titleField = $model->{commonFields}->{title};
#                my $titleInfo = $self->{parent}->{model}->{fieldsInfo}->{$titleField};
#                my $titleText = $model->getDisplayedText($titleInfo->{label});
#                $self->{titleGroup}->setLabel($titleText);
#            }

            return if $list && ($self->{currentPluginList} eq $list);
            $self->{currentPluginList} = $list;

            my $pluginGroup = undef;
            my $i = 0;
            my @newData;
            
            $self->{model} = $model;
            
            if ($model)
            {
                foreach (sort keys %{$model->getAllPlugins})
                {
                    my $plugin = $model->getPlugin($_);
                    if ((!$list) || ($plugin->getName eq $plugins_array[$i]))
                    {
                        push @newData, [$plugin->getName, "\n".$plugin->getLang."\n",
                                        $plugin->getSearchFields($model), $plugin->getAuthor];
                        $i++;
                    }
                }
            }
            @{$self->{pluginsList}->{data}} = @newData;
            $self->{pluginsList}->select(0);
        }
    }
     
    {
        package GCAllPluginsDialog;
        
        use base 'GCModalDialog';
    
        sub show
        {
            my $self = shift;
            $self->SUPER::show();
            $self->show_all;
            ($self->action_area->get_children)[1]->hide_all;
            my $response = $self->run;
            $self->hide;
        }
        
        sub new
        {
            my ($proto, $parent) = @_;
            my $class = ref($proto) || $proto;
            my $self  = $class->SUPER::new($parent,
                                           $parent->{lang}->{PluginsFrame},
                                           'gtk-close'
                                          );
    
            bless ($self, $class);

            $self->{parent} = $parent;
            $self->{factory} = $parent->{modelsFactory};

            my @columnsNames = ('Name', 'Lang', 'SearchFields', 'Author');
            $self->{pluginsModel} = new Gtk2::TreeStore(map {'Glib::String'} @columnsNames);
            $self->{pluginsList} = Gtk2::TreeView->new_with_model($self->{pluginsModel});

            my @columns;
            my $i = 0;
            for my $col(@columnsNames)
            {
                my $column = Gtk2::TreeViewColumn->new_with_attributes($parent->{lang}->{'Plugins'.$col},
                                                                       Gtk2::CellRendererText->new,
                                                                       'text' => $i);
                $column->set_resizable(1);
                $self->{pluginsList}->append_column($column);
                $i++;
            }

            $self->{pluginsList}->set_border_width(5);
            $self->{pluginsList}->set_rules_hint(1);

            my $scrollPanelList = new Gtk2::ScrolledWindow;
            $scrollPanelList->set_border_width(5);
            $scrollPanelList->set_policy ('never', 'automatic');
            $scrollPanelList->set_shadow_type('etched-in');
            $scrollPanelList->add($self->{pluginsList});

            $self->{useThisSite} = Gtk2::CheckButton->new($parent->{lang}->{PluginsUseSite});

            my $pluginBox = new Gtk2::VBox(0,0);
            $pluginBox->pack_start($scrollPanelList, 1, 1, 0);

            for my $modelInfo (@{$self->{factory}->getDefaultModels})
            {
                my $modelIter = $self->{pluginsModel}->append(undef);
                $self->{pluginsModel}->set($modelIter,
                                           (0 => $modelInfo->{description})
                                          );

                my $model = $self->{factory}->getModel($modelInfo->{name});
                for my $pluginName (sort keys %{$model->getAllPlugins})
                {
                    my $plugin = $model->getPlugin($pluginName);
                    my $pluginIter = $self->{pluginsModel}->append($modelIter);
                    my %data;
                    $i = 0;
                    my $method;
                    ($method = 'get'.$_, $data{$i++} = $plugin->$method($model))
                        foreach (@columnsNames);
                    $self->{pluginsModel}->set($pluginIter, %data);
#                                               (
#                                                0 => $plugin->getName,
#                                                1 => $plugin->getLang,
#                                                2 => $plugin->getSearchFieldsAsString($model),
#                                                4 => $plugin->getAuthor
#                                               )
#                                              );
                }
            }

            $self->vbox->pack_start($pluginBox, 1, 1, 0);

            $self->set_default_size(1,550);
            
            return $self;
        }
    
        sub setModel
        {
            my ($self, $model, $list) = @_;
            $model ||= $self->{parent}->{model};
            my @plugins_array = sort split (",", $list) if $list;
    
            my @fields = @{$model->getSearchFields};
            if ($#fields < 1)
            {
                $self->{hideFieldsList} = 1;
            }
            else
            {
                $self->{hideFieldsList} = 0;
                my @values;
                foreach (@fields)
                {
                    push @values, {value => $_,
                                   displayed => $model->getDisplayedText($model->{fieldsInfo}->{$_}->{label})};
                }
                $self->{fieldsList}->setValues(\@values);
            }

            return if $list && ($self->{currentPluginList} eq $list);
            $self->{currentPluginList} = $list;

            my $pluginGroup = undef;
            my $i = 0;
            my @newData;
            
            $self->{model} = $model;
            
            if ($model)
            {
                foreach (sort keys %{$model->getAllPlugins})
                {
                    my $plugin = $model->getPlugin($_);
                    if ((!$list) || ($plugin->getName eq $plugins_array[$i]))
                    {
                        push @newData, [$plugin->getName, "\n".$plugin->getLang."\n", $plugin->getAuthor];
                        $i++;
                    }
                }
            }
            @{$self->{pluginsList}->{data}} = @newData;
            $self->{pluginsList}->select(0);
        }
    }

    {
        #Class that is used to let user select
        #plugins he wants to use in a multi-site search.
        package GCMultiSiteDialog;

        use base 'GCDoubleListDialog';
        
        sub getInitData
        {
            my $self = shift;
            
            return $self->{model}->getPluginsNames;
        }
        
        sub getData
        {
            my $self = shift;
            
            my @array = split m/,/, $self->{model}->{preferences}->multisite;
            return \@array;
        }
        
        sub saveList
        {
            my ($self, $list) = @_;
            
            my $value = join ',', @$list;
            $self->{model}->{preferences}->multisite($value);
        }
        
        sub getPlugin
        {
            my ($self, $idx) = @_;
            $self->init;
            return $self->{usedArray}->[$idx];
        }
        
        sub getPluginsNumber
        {
            my $self = shift;
            $self->init;
            return scalar @{$self->{usedArray}};
        }
                
        sub preFill
        {
            my $self = shift;
            
            my @data;
            foreach (@{$self->{model}->getPluginsNames})
            {
                push @data, $_ if $self->{model}->getPlugin($_)->getLang eq $self->{options}->lang;
            }
            $self->setListData(\@data);
        }

        sub getUnusedLabel
        {
            my $self = shift;
            
            return $self->{parent}->{lang}->{MultiSiteUnused};
        }
        sub getUsedLabel
        {
            my $self = shift;
            
            return $self->{parent}->{lang}->{MultiSiteUsed};
        }

        sub setModel
        {
            my ($self, $model) = @_;
            
            $self->{model} = $model;
        }               

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

            $self->setModel($model);

            if (! $self->{model}->{preferences}->exists('multisite'))
            {
                $self->preFill;
                $self->saveList(\@{$self->{usedArray}});
                $self->{initialized} = 1;
            }

            my $langButton = new Gtk2::Button($parent->{lang}->{MultiSiteLang});
            $langButton->set_border_width($GCUtils::margin);
            $langButton->signal_connect('clicked' => sub {
                $self->preFill;
            });
            my $clearButton = new Gtk2::Button($parent->{lang}->{MultiSiteClear});
            $clearButton->set_border_width($GCUtils::margin);
            $clearButton->signal_connect('clicked' => sub {
                $self->clearList;
            });            
            
            $self->{vboxUnused}->pack_start($langButton, 0, 0, 0);
            $self->{vboxUsed}->pack_start($clearButton, 0, 0, 0);
            
            return $self;
        }
    }
    
}

{
    package GCResultsDialog;
    use base 'GCModalDialog';

    sub show
    {
        my $self = shift;
        for my $i (0..$self->{nbCols} - 1)
        {
            $self->{results}->get_column($i)->set_sort_indicator(0);
        }
        $self->{order} = 1;
        $self->{sort} = '';

        $self->{validated} = 0;
        $self->SUPER::show();
        $self->show_all;
        $self->{nextButton}->hide if !$self->{withNext};
        $self->{multipleSelectionLabel}->hide if ! $self->{multipleSelection};
        my $ended = 0;
        my $code;
        while (!$ended)
        {
            $code = $self->run;
            if ($code eq 'ok')
            {
                $self->{validated} = 1;
                $self->{itemsIndexes} = [];
                my @idxs = $self->{results}->get_selected_indices;
                foreach my $idx(@idxs)
                {
                    push @{$self->{itemsIndexes}}, $self->{items}->[$idx]->{'#'};
                }
            }
            elsif ($code eq 'yes')
            {
                my @idx = $self->{results}->get_selected_indices;
                my $itemIndex = $self->{items}->[$idx[0]]->{'#'};
                $self->{parent}->getItemInfo($self->{plugin}, $itemIndex, 1);
            }
            $ended = 1 if ($code eq 'ok') || ($code eq 'cancel') || ($code eq 'delete-event') || ($code eq 'no');
        }
        $self->hideTooltip;
        $self->hide;
        return ($code eq 'no');
    }

    sub getItemsIndexes
    {
        my $self = shift;
        return $self->{itemsIndexes};
    }

    sub setMultipleSelection
    {
        my ($self, $activate) = @_;
        return if !$self->{results};
        $self->{results}->get_selection->set_mode ($activate ? 'multiple' : 'single');
        $self->{multipleSelection} = $activate;
    }

    sub setWithNext
    {
        my ($self, $value) = @_;
        
        $self->{withNext} = $value;
    }

    sub setSearchPlugin
    {
        my ($self, $plugin) = @_;
       
        $self->{plugin} = $plugin;
        $self->set_title($self->{parent}->{lang}->{ResultsTitle}.' ('.$plugin->getName.')');
 
        for my $i (0..$self->{nbCols} - 2)
        {
            # If plugin is a multi-pass plugin, then update the column headers for this pass
            $plugin->getReturnedFields() if $plugin->getNumberPasses > 1;

            $self->{results}->get_column($i)->set_visible($plugin->hasField($self->{fields}->[$i]));
        }
 
        if ($plugin->getExtra)
        {
            $self->{results}->get_column($self->{nbCols} - 1)->set_visible(1);
            $self->{results}->get_column($self->{nbCols} - 1)->set_title($plugin->getExtra);
            $self->{withExtra} = 1;
        }
        else
        {
            $self->{results}->get_column($self->{nbCols} - 1)->set_visible(0);
            $self->{withExtra} = 0;
        }
    }

    sub new
    {
        my ($proto, $parent) = @_;
        my $class = ref($proto) || $proto;
        my $self  = $class->SUPER::new($parent,
                                       $parent->{lang}->{ResultsTitle},
                                       undef, 0,
                                       'gtk-zoom-in' => 'yes'
                                      );
        bless ($self, $class);

        $self->{results} = 0;
        $self->{withExtra} = 0;
        $self->{withNext} = 0;
        $self->{multipleSelection} = 0;
        $self->{nextButton} = Gtk2::Button->new_from_stock('gtk-go-forward');
        $parent->{tooltips}->set_tip($self->{nextButton},
                                     $parent->{lang}->{ResultsNextTip});
        $self->add_action_widget($self->{nextButton}, 'no');

        $self->{parent} = $parent;

        $self->set_modal(1);
        $self->set_position('center');

        $self->{scrollPanelList} = new Gtk2::ScrolledWindow;
        $self->{scrollPanelList}->set_policy ('never', 'automatic');
        $self->{scrollPanelList}->set_shadow_type('etched-in');
                                            
        $self->vbox->pack_start($self->{scrollPanelList}, 1, 1, 0);
        $self->vbox->pack_start(Gtk2::HSeparator->new, 0, 0, 0);
        my $fillBox = new Gtk2::HBox(0,0);
        $self->{multipleSelectionLabel} = GCLabel->new('<i>'.$parent->{lang}->{ResultsInfo}.'</i>');
        $self->{multipleSelectionLabel}->set_line_wrap(1);
        $self->{multipleSelectionLabel}->set_justify('center');
        $self->{multipleSelectionLabel}->set_padding($GCUtils::margin, 0);
        $fillBox->pack_start(Gtk2::HBox->new(0,0),1,1,0);
        $fillBox->pack_start($self->{multipleSelectionLabel}, 0, 0, $GCUtils::margin);
        $fillBox->pack_start(Gtk2::HBox->new(0,0),1,1,0);
        $self->vbox->pack_start($fillBox, 0 , 0, $GCUtils::margin);
         
        # To create a tooltip as it is not implemented directly in Gtk2
        $self->{tooltipLabel} = Gtk2::Label->new;
        $self->{tooltipLabel}->set_line_wrap(1);
        $self->{tooltipLabel}->set_padding($GCUtils::margin, $GCUtils::margin);
        $self->{tooltip} = Gtk2::Window->new('popup');
        $self->{tooltip}->set_decorated(0);
        $self->{tooltip}->set_sensitive(1);
        $self->{tooltip}->modify_fg('normal', Gtk2::Gdk::Color->parse('#000000'));
        $self->{tooltip}->modify_bg('normal', Gtk2::Gdk::Color->parse('#ffffbf'));
        $self->{tooltip}->set_position('mouse');
        $self->{tooltip}->add($self->{tooltipLabel});
        $self->{tooltip}->signal_connect('button_press_event' => sub {
            $self->hideTooltip;
            if ($self->{results})
            {
                shift;
                $self->{results}->signal_emit('button_press_event', @_);
            }
        });
        $self->{tooltip}->signal_connect('motion-notify-event' => sub {
            my ($widget, $event) = @_;
            my ($x, $y) = ($event->x_root, $event->y_root);
            my ($xoffset, $yoffset) = $widget->window->get_root_origin;
            my $allocation = $widget->allocation();
            my $x1 = $xoffset + 2 * $allocation->x;
            my $y1 = $yoffset + 2 * $allocation->y;
            my $x2 = $x1 + $allocation->width;
            my $y2 = $y1 + $allocation->height;
            my $isInWidget = ($x > $x1 && $x < $x2 && $y > $y1 && $y < $y2);
            
            return if $isInWidget;
            if ($self->{results})
            {
                shift;
                $self->{results}->signal_emit('motion-notify-event', @_);
            }            
        });
        $self->{tooltipDisplayed} = 0;        
 
        return $self;
    }

    sub setModel
    {
        my ($self, $model, $fieldsInfo) = @_;
        
        if ($self->{results})
        {
            $self->{scrollPanelList}->remove($self->{results});
            $self->{results}->destroy;
        }

        
        $self->{fields} = [];
        my @cols;
        
        foreach my $field(@{$model->{resultsFields}})
        {
            push @cols, ($fieldsInfo->{$field}->{displayed} => 'text');
            push @{$self->{fields}}, $field;
        }
        
        # Extra column
        push @cols, ('' => 'text');
        push @{$self->{fields}}, 'extra';

        $self->{nbCols} = scalar @{$self->{fields}};
        
        $self->{results} = Gtk2::SimpleList->new(@cols);
        
        $self->{results}->set_rules_hint(1);
        $self->{results}->set_headers_clickable(1);

        $self->{order} = 1;
        $self->{sort} = '';
        
        for my $i (0..$self->{nbCols} - 1)
        {
            $self->{results}->get_column($i)->set_sort_indicator(0);
            $self->{results}->get_column($i)->set_resizable(1);
            $self->{results}->get_column($i)->signal_connect('clicked' => sub {
                $self->sort($self->{fields}->[$i]);
            });
            $self->{results}->get_column($i)->{column_number} = $i;
        }
         
       $self->{scrollPanelList}->add($self->{results});

       $self->{results}->signal_connect(row_activated => sub {
            my ($sl, $path, $column) = @_;
            $self->response('ok');
        });
        $self->{results}->signal_connect('motion-notify-event' => sub {
            my ($widget, $event) = @_;
            $self->displayTooltip($event);
        });
        $self->{results}->signal_connect('leave-notify-event' => sub {
            my ($widget, $event) = @_;
            # It corresponds to a leave of the whole widget not from a single cell
            $self->hideTooltip if $event->detail eq 'ancestor';
        });

    }

    sub sort
    {
        my ($self, $type) = @_;
        
        my $col = 0;
        
        for my $i (0..$self->{nbCols} - 1)
        {
            $self->{results}->get_column($i)->set_sort_indicator(0);
            $col = $i if $self->{fields}->[$i] eq $type;
        }
        
        my @items = @{$self->{items}};
        
        if ($self->{sort} eq $type)
        {
            $self->{order} = 1 - $self->{order}
        }
        else
        {
            $self->{order} = 1;
        }

        @items = sort {$a->{$type} cmp $b->{$type}} @items;
        
        @items = reverse @items if ! $self->{order};
        
        $self->{results}->get_column($col)->set_sort_indicator(1);
        $self->{results}->get_column($col)->set_sort_order($self->{order} ? 'ascending' : 'descending');
        
        $self->setList('',@items);

        $self->{sort} = $type;
    }
    
    sub setList
    {
        my ($self, $title, @items) = @_;
        
        $self->set_title($self->get_title . ' - '.$title) if $title;
        
        $self->{items} = \@items;

        @{$self->{results}->{data}} = ();
        $self->{tooltipsStrings} = {};

        my $idx = 0;
        my $col = 0;
        foreach my $item (@items)
        {
            my $infos = [];
            $col = 0;
            foreach my $field(@{$self->{fields}})
            {
                my $value = '';
                $value = $item->{$field} if exists $item->{$field};
                (my $shortField = $value) =~ s/(.{40}).*/$1.../;
                push @$infos, $shortField."\n";
                # We store a tooltip is the text has been truncated
                $self->{tooltipsStrings}->{$idx}->{$col} = $value
                    if $value ne $shortField;
                $col++;
            }
            $item->{'#'} = $idx if ! exists $item->{'#'};
            push @{$self->{results}->{data}}, $infos;
            #push @{$self->{tooltipsStrings}}, $item->{$self->{fields}->[0]};
            $idx++;
        }
        $self->{results}->select(0);
        $self->{results}->columns_autosize;
        $self->set_default_size(-1,400);
    }
 
 
     sub displayTooltip 
     {        
        my ($self, $event) = @_;
        my ($path, $column, $cell_x, $cell_y) = $self->{results}->get_path_at_pos ($event->x, $event->y);
        if ($path)
        {
            my $model = $self->{results}->get_model;
            my $col = $column->{column_number};
            my $row = $path->to_string();
            
            # If a new cell is selected, then hide the tooltip
            # It'll be re-shown as required by the code down under
            if ($self->{selectedRow} ne $row or $self->{selectedCol} != $col)
            {
                $self->hideTooltip;
                $self->{selectedRow} = $row;
                $self->{selectedCol} = $col;
            }
            else
            {
                return;
            }
            if ($row ne '')
            {
                # Pick that popup string from our hash
                #my $str = $popup_hash->{$row}->{$i};
                my $str = $self->{tooltipsStrings}->{$row}->{$col};
                if ($str)
                {
                    $self->{tooltipLabel}->set_label($str);
                    if (!$self->{tooltipDisplayed})
                    {
                        $self->{tooltip}->show_all;
                        Gtk2->grab_add($self->{tooltip});
                        Gtk2::Gdk->pointer_grab(
                            $self->{tooltip}->window, 1,
                            [qw/button-press-mask button-release-mask pointer-motion-mask/],
                            undef, undef, 0);
                        Gtk2::Gdk->keyboard_grab ($self->{tooltip}->window, 0, 0);
                        $self->{tooltip}->grab_focus;
                        my ($thisx, $thisy) = $self->{tooltip}->window->get_origin;
                        # The window to be a bit away from the mouse pointer.
                        $self->{tooltip}->move($thisx, $thisy-20);
                        $self->{tooltipDisplayed} = 1;
                    } 
                } 
            }
            return 0;
        }
    }
    
    sub hideTooltip
    {
        my $self = shift;
        if ($self->{tooltipDisplayed})
        {
            Gtk2->grab_remove($self->{tooltip});
            $self->{tooltip}->hide;
            $self->{tooltipDisplayed} = 0;
        }
     }
   
}

{
    package GCImportFieldsDialog;
    use base 'GCModalDialog';

    sub setReadOnly
    {
        my ($self, $value) = @_;
        $self->{readOnly} = $value;
        my @children = $self->{table}->get_children;
        foreach (@children)
        {
            $_->set_sensitive(!$value) if ($_->get_name eq 'GtkCheckButton')
                                       && (!$_->{mandatory});
        }
        $self->set_title($value ? $self->{parent}->{lang}->{ResultsPreview}
                                : $self->{parent}->{lang}->{ImportWindowTitle});
    }

    sub info
    {
        my $self = shift;
        
        my $fieldsInfo = $self->{parent}->{model}->{fieldsInfo};
        
        if (@_)
        {
            my $info = shift;

            $self->{info} = $info;
        
            my @children = $self->{table}->get_children;
            foreach (@children)
            {
                $_->set_text('') if $_->get_name eq 'GtkEntry';
            }

            my $fieldsInfo = $self->{parent}->{model}->{fieldsInfo};        
            foreach (keys %{$info})
            {
                my $tmp = $info->{$_};
                if ($fieldsInfo->{$_}->{type} =~ /list/)
                {
                    $tmp = GCPreProcess::multipleList($tmp, $fieldsInfo->{$_}->{type});
                }
                if ($fieldsInfo->{$_}->{values})
                {
                    $tmp = $self->{parent}->{model}->getDisplayedValue($fieldsInfo->{$_}->{values},
                                                                       $tmp);
                }
                if ($_ ne $self->{urlField})
                {
                    $tmp =~ s/\n/ /g;
                    $tmp =~ s/(.{50}).*/$1.../m;
                }
                $self->{$_}->set_text($tmp)
                    if $self->{$_};
            }
        }
        else
        {
            my $ignore = $self->{parent}->{ignoreString};
            
            foreach my $field(@{$self->{parent}->{model}->{fieldsNames}})
            {
                next if $fieldsInfo->{$field}->{imported} ne 'true';
                next if ($fieldsInfo->{$field}->{type} eq 'url');
                if (! $self->{$field.'Cb'}->get_active)
                {
                    unlink $self->{info}->{$field}
                        if ($fieldsInfo->{$field}->{type} eq 'image')
                        && ($self->{info}->{$field} !~ m|^http://|);
                    $self->{info}->{$field} = $ignore;
                }
            }            

            return $self->{info};
        }
    }
    
    sub showImage
    {
        use File::Temp qw/ :POSIX  /;
        
        my ($self, $field) = @_;
        
        my $location = $self->{info}->{$field};
        if ($location =~ m|^http://|)
        {
            my ($name,$path,$suffix) = File::Basename::fileparse($location, "\.gif", "\.jpg", "\.jpeg", "\.png");
            $self->window->set_cursor(Gtk2::Gdk::Cursor->new('watch'));
            Gtk2->main_iteration while (Gtk2->events_pending);
            (my $tmpFile = tmpnam) .= $suffix;
            GCUtils::downloadFile($location, $tmpFile, $self->{parent});
            $self->window->set_cursor(Gtk2::Gdk::Cursor->new('left_ptr'));
            $self->{parent}->launch($tmpFile, 'image');
            $self->{info}->{$field} = $tmpFile;
        }
        else
        {
            $self->{parent}->launch($location, 'image');
        }
    }
    
    sub show
    {
        my $self = shift;

        $self->SUPER::show();
        $self->show_all;
        if ($self->{readOnly})
        {
            $self->{selectAll}->hide;
            $self->{selectNone}->hide;
            ($self->action_area->get_children)[1]->hide;
        }
        
        foreach (keys %{$self->{imagesButton}})
        {
            $self->{$_}->hide if $self->{info}->{$_};
            $self->{imagesButton}->{$_}->hide if ! $self->{info}->{$_};
        }
        my $response = $self->run;
        $self->hide;
        return ($response eq 'ok');
    }

    sub createItem
    {
        my ($self, $field, $row, $mandatory) = @_;
        my $fieldsInfo = $self->{parent}->{model}->{fieldsInfo};
        $self->{$field.'Cb'} = new Gtk2::CheckButton($self->{parent}->{model}->getDisplayedText($fieldsInfo->{$field}->{label}));
        if ($mandatory)
        {
            $self->{$field.'Cb'}->set_sensitive(0);
            $self->{$field.'Cb'}->set_active(1);
            $self->{$field.'Cb'}->{mandatory} = 1;
        }
        $self->{$field} = new Gtk2::Entry;
        $self->{$field}->set_editable(0);
        $self->{table}->attach($self->{$field.'Cb'}, 0, 1, $row, $row + 1, 'fill', ['fill', 'expand'], 0, 0);
        $self->{table}->attach($self->{$field}, 1, 2, $row, $row + 1, ['fill', 'expand'], 'fill', 0, 0);
    }

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

        bless ($self, $class);
        
        $self->{parent} = $parent;
 
        $self->set_modal(1);
        $self->set_position('center');
        $self->set_default_size(1,500);

        $self->{table} = new Gtk2::Table(1, 2, 0);
        $self->{table}->set_row_spacings($GCUtils::halfMargin);
        $self->{table}->set_col_spacings($GCUtils::margin);
        $self->{table}->set_border_width($GCUtils::margin);

        $self->{scrollPanelList} = new Gtk2::ScrolledWindow;
        $self->{scrollPanelList}->set_policy ('never', 'automatic');
        $self->{scrollPanelList}->set_shadow_type('none');
        $self->{scrollPanelList}->add_with_viewport($self->{table});

        $self->vbox->pack_start($self->{scrollPanelList},1,1,$GCUtils::margin);

        $self->setModel($parent->{model});

        return $self;
    }
    
    sub setModel
    {
        my ($self, $model) = @_;
        
        my $parent = $self->{parent};
        
        $self->{model} = $model;
        my $fieldsInfo = $model->{fieldsInfo};
        $self->{urlField} = $self->{parent}->{model}->{commonFields}->{url};

        foreach ($self->{table}->get_children)
        {
            $self->{table}->remove($_);
            $_->destroy;
        }

        my $row = 0;
        
        my @picFields;
        my $field;
        foreach $field(@{$model->{fieldsNames}})
        {
            next if $fieldsInfo->{$field}->{imported} ne 'true';
            next if ($field eq $self->{urlField});
            if ($fieldsInfo->{$field}->{type} eq 'image')
            {
                push @picFields, $field;
                next;
            }
            $self->createItem($field, $row);
            $row++;
        }
        
        foreach $field(@picFields)
        {
            $self->createItem($field, $row);
            $self->{imagesButton}->{$field} = new Gtk2::Button($parent->{lang}->{ImportViewPicture});
            $self->{imagesButton}->{$field}->signal_connect('clicked' => sub {
                $self->showImage($field);
            });
            $self->{table}->attach($self->{imagesButton}->{$field}, 1, 2, $row, $row + 1, 'fill', 'fill', 0, 0);
            $row++;
        }

        if ($self->{urlField})
        {
            $self->createItem($self->{urlField}, $row, 1);
            $row++;
        }

        $row++;

        $self->{selectAll} = new Gtk2::Button($parent->{lang}->{ImportSelectAll});
        $self->{selectAll}->signal_connect('clicked' => sub {
                my @children = $self->{table}->get_children;
                foreach (@children)
                {
                    $_->set_active(1) if $_->get_name eq 'GtkCheckButton';
                }
        });
        $self->{table}->attach($self->{selectAll}, 0, 1, $row, $row + 1, 'fill', 'fill', 0, 0);
        $row++;
        $self->{selectNone} = new Gtk2::Button($parent->{lang}->{ImportSelectNone});
        $self->{selectNone}->signal_connect('clicked' => sub {
                my @children = $self->{table}->get_children;
                # Remove 2 items corresponding to website to keep it checked
                splice @children, 2, 2;
                foreach (@children)
                {
                    $_->set_active(0) if $_->get_name eq 'GtkCheckButton';
                }
        });
        $self->{table}->attach($self->{selectNone}, 0, 1, $row, $row + 1, 'fill', 'fill', 0, 0);


    }
    
}

1;
