package GCPanel;

###################################################
#
#  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 utf8;
use Gtk2;
use GCBorrowings;
use GCGraphicComponents;

use strict;

{
    package GCItemPanel;
    use base "Gtk2::Frame";
    
    sub new
    {
        my ($proto, $parent, $options, $layout) = @_;
        my $class = ref($proto) || $proto;
        my $self  = $class->SUPER::new();

        bless ($self, $class);

        $self->{disableBorrowerChange} = 0;
        $self->{autoUpdate} = 1;
        $self->{previouslyHidden} = 0;

        $self->{expanders} = [];
        $self->{dateFields} = [];
        $self->{longTextFields} = [];
        $self->{formatted} = [];
        #Stores for each fields the widgets which depends on it
        # (used for formatted and launcher)
        $self->{dependencies} = {};

        $self->{parent} = $parent;
        $self->{lang} = $parent->{lang};
        $self->{options} = $options;
        $self->{layout} = $layout;
        $self->set_shadow_type('none');
        
        #$self->{defaultImage} = $parent->getDefaultImage;
        $self->{tooltips} = $parent->{tooltips};
        
        $self->{infoBox} = new Gtk2::VBox(1,0);
        $self->{viewAllBox} = new Gtk2::EventBox;
        $self->{viewAllVBox} = new Gtk2::VBox(0,0);
        $self->{viewAll} = new Gtk2::HBox(0,0);
        $self->{viewAllButton} = new Gtk2::Button->new_from_stock('gtk-refresh');
        $self->{viewAll}->pack_start($self->{viewAllButton},1,0,50);
        $self->{viewAllVBox}->pack_start($self->{viewAll},0,0,0);
        $self->{viewAllBox}->add($self->{viewAllVBox});
        $self->{viewAllButton}->signal_connect('clicked' => sub {
            $self->{parent}->allItems;
        });

        $self->{margin} = $GCUtils::margin;
        $self->{halfMargin} = $GCUtils::halfMargin;
        $self->{quarterMargin} = $GCUtils::quarterMargin;

        return $self;
    }
    
    sub loadStyles
    {
        my ($self, $style) = @_;
    }    


    sub setExpandersMode
    {
        my ($self, $mode) = @_;
        $_->setMode($mode)
            foreach (@{$self->{expanders}});
    }

    sub setDateFormat
    {
        my ($self, $format) = @_;
        foreach (@{$self->{dateFields}})
        {
            $_->setFormat($format);
            $_->resetChanged;
        }
    }

    sub setSpellChecking
    {
        my ($self, $activate, $lang) = @_;
        foreach (@{$self->{longTextFields}})
        {
            $_->setSpellChecking($activate, $lang);
        }
    }

    sub createContent
    {
        my ($self, $model) = @_;
        return if $self->{model}
              && ($self->{model}->getName eq $model->getName);

        if ($self->{model})
        {
            my @children = $self->{mainBox}->get_children;
            $self->{mainBox}->remove($_) foreach (@children);
            foreach (@{$self->{toDestroy}})
            {
                $_->destroy if $_;
            }
        }

        $self->{model} = $model;
        $self->{fieldsInfo} = $self->{model}->{fieldsInfo};
        $self->{toDestroy} = [];
        foreach (@{$model->{fieldsNames}})
        {
            $self->{$_} = $self->createItem($self->{model}->{fieldsInfo}->{$_});
            push @{$self->{toDestroy}}, $self->{$_};
            $self->{$_.'Label'} = $self->createLabel($self->{model}->{fieldsInfo}->{$_}, $self->{withSeparator});
            push @{$self->{toDestroy}}, $self->{$_.'Label'};
        }
        $self->createSpecial;

        $self->{displayedValues} = {};
        $self->createLayout($self->{layout}, $self->{layoutBox});

        $self->show_all;
    }
    
    sub initShow
    {
        my ($self, $show, $hasToShow) = @_;

        $self->{show} = $show;
       
        if ($hasToShow)
        {
            $self->show_all;
            $self->{infoBox}->hide;
            return 1;
        }
        else
        {
            $self->hide(0);
            $self->{previouslyHidden} = 1;
            return 0;
        }
    }
    
    sub show
    {
        my $self = shift;
        $self->{mainBox}->show;
        $self->{infoBox}->hide;
        $self->setShowOption($self->{show}, 1) if $self->{previouslyHidden};
        $self->{previouslyHidden} = 0;
    }
    
    sub setShowOption
    {
        my ($self, $show, $hasToShow) = @_;
        return if ! $self->initShow($show, $hasToShow);
        my %parentsToHide;
        foreach (keys %{$show})
        {
            $self->{$_.'Label'}->hide if (! $show->{$_}) && ($self->{$_.'Label'});
            my $widget = $self->{$_};
            if ($widget)
            {
                if ($show->{$_})
                {
                    # Useful for GCLinkedComponent
                    $widget->show if !$widget->isa('Gtk2::Widget');
                    #$parentsToHide{$widget->{realParent}} = 0;
                    my $parent = $widget->{realParent};
                    while ($parent)
                    {
                        $parentsToHide{$parent} = 0;
                        $parent = $parent->{realParent};
                    }
                    foreach my $dependency(@{$self->{dependencies}->{$_}})
                    {
                        $parentsToHide{$dependency->{realParent}} = 0;
                    }
                }
                else
                {
                    $widget->hide;
                    $parentsToHide{$widget->{realParent}} = $widget->{realParent} if ! exists $parentsToHide{$widget->{realParent}};
                    foreach my $dependency(@{$self->{dependencies}->{$_}})
                    {
                        $dependency->hide;
                    }
                }
            }
        }

        foreach (keys %parentsToHide)
        {
            if ($parentsToHide{$_})
            {
                $parentsToHide{$_}->hide;
                $parentsToHide{$_}->{realParent}->hide
                    if ($parentsToHide{$_}->{realParent})
                    && ($parentsToHide{$_}->{realParent}->isa('GCExpander'));
            }
        }
        
        $self->setShowOptionForSpecials($show, $hasToShow);
    }

    sub setShowOptionForSpecials
    {
        my ($self, $show, $hasToShow) = @_;
    }

    sub hide
    {
        my ($self, $filtering) = @_;
        $self->{infoBox}->show_all;
        if ($filtering)
        {
            $self->{warning}->set_markup('<b>'.$self->{lang}->{AllItemsFiltered}.'</b>');
        }
        else
        {
            $self->{warning}->set_markup($self->{lang}->{Warning}) if !$filtering;
            $self->{viewAllBox}->hide;
        }
        $self->{mainBox}->hide;
    }
    
    sub showMe
    {
        my $self = shift;
        my $parent = $self->parent;
        while ($parent)
        {
           last if ($parent->isa('Gtk2::Window'));
           $parent = $parent->parent;
        }
        $parent->present if $parent;
    }
    
    sub isReadOnly
    {
        return 1;
    }
    
    sub getAsHash
    {
        my $self = shift;
        my $hash = {};
        $hash->{$_} = $self->$_ foreach(@{$self->{parent}->{model}->{fieldsNames}});
        return $hash;
    }
    
    sub getValue
    {
        my ($self, $field) = @_;
        
        return $self->$field;
    }
    
    sub disableBorrowerChange
    {
        my $self = shift;
        $self->{disableBorrowerChange} = 1;
    }
    
    sub disableAutoUpdate
    {
        my $self = shift;
        $self->{autoUpdate} = 0;
    }

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

        foreach (@{$self->{expanders}})
        {
            $self->setExpanderLabel($_, 1);
        }
        foreach (@{$self->{formatted}})
        {
            $self->setFormattedLabel($_, 1, $info);
        }
    }

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

        my $label = $self->{model}->getDisplayedText($info->{label});
        $label .= $self->{parent}->{lang}->{Separator} if $withSeparator;
        my $widget = new Gtk2::Label($label);
        $widget->set_alignment(0, 0.5);
        return $widget;
    }

    sub createExpander
    {
        my ($self, $label) = @_;

        return new GCExpander($label);
    }
    
    sub formatLabel
    {
        my ($self, $format) = @_;

        # Allow formatting using %fields[format]% 
        (my $label = $format) =~ s/%(.*?)(\[(.*?)\])*%/$3 ? sprintf($3,$self->{$1}->getValue(1)) : $self->{$1}->getValue(1)/eg;

        $label =~ s/@(.*?)@/$self->{model}->getDisplayedText($1)/eg;
        return $label;
    }
    
    sub setExpanderLabel
    {
        my ($self, $expander, $init) = @_;

        if ($expander->{collapsedTemplate}
        && ($expander->get_expanded xor $init))
        {
            #$expander->setValue($expander->{originalLabel}.$self->{parent}->{lang}->{Separator}.
            #                    $self->formatLabel($expander->{collapsedTemplate}));
            $expander->setValue($expander->{originalLabel}.$self->{parent}->{lang}->{Separator},
                                $self->formatLabel($expander->{collapsedTemplate}));
        }
        else
        {
            $expander->setValue($expander->{originalLabel});
        }
    }
    
    sub setAsFormatted
    {
        my ($self, $widget, $name, $format) = @_;
        $widget->{format} = $format;
        $widget->{name} = $name;
        push @{$self->{formatted}}, $widget;
        while ($format =~ /%(.*?)%/g)
        {
            push @{$self->{dependencies}->{$1}}, $widget;
        }
    }

    sub setFormattedLabel
    {
        my ($self, $label, $init, $info) = @_;

        my $name = $label->{name};
        $self->$name($self->formatLabel($label->{format}));
        # We store here the value so it could be used by caller if needed
        # The user is GCData::updateCurrent
        $info->{$name} = $self->$name if defined $info;
    }

    sub prepareDate
    {
        my ($self, $value) = @_;

        return GCUtils::timeToStr($value, $self->{parent}->{options}->dateFormat)
    }

    sub addToContainer
    {
        my ($self, $container, $widget, $info) = @_;
        $info->{expand} = 'default' if !exists $info->{expand};
        if ($container->isa('Gtk2::Notebook'))
        {
            $container->append_page($widget,
                                    $self->{model}->getDisplayedText($info->{title}));
        }
        elsif ($container->isa('Gtk2::Table'))
        {
            my $left = $info->{col};
            my $right = $left + ($info->{colspan} ? $info->{colspan} : 1);
            my $top = $info->{row};
            my $bottom = $top + ($info->{rowspan} ? $info->{rowspan} : 1);
            my $fill = [];
            push @$fill, 'fill' if $info->{expand} ne 'false';
            push @$fill, 'expand' if $info->{expand} eq 'true';
            if ($info->{expand} eq 'shrink')
            {
                $fill = ['expand', 'fill'];
                my $hbox = new Gtk2::HBox(0,0);
                $hbox->pack_start($widget,0,0,0);
                $widget = $hbox;
            }
            if ($info->{expand} eq 'horizontal')
            {
                $container->attach($widget,
                                   $left, $right, $top, $bottom,
                                   ['expand', 'fill'], [],
                                   0, 0);
            }
            else
            {
                $container->attach($widget,
                                   $left, $right, $top, $bottom,
                                   $fill, $fill,
                                   0, 0);
            }
        }
        else
        {
            my $expand = ($info->{expand} eq 'true') ? 1 : 0;
            if ($container->isa('Gtk2::VBox'))
            {
                my $margin = $self->{halfMargin};
                $margin = 0 if (exists $info->{nomargin}) && ($info->{nomargin} eq 'true');
                if ((exists $info->{place}) && ($info->{place} eq 'end'))
                {
                    $container->pack_end($widget, $expand, $expand, $margin);
                }
                else
                {
                    $container->pack_start($widget, $expand, $expand, $margin);
                }
            }
            else
            {
                if (($container->get_children) && (!$container->get_homogeneous))
                {
                    my $marginBox = new Gtk2::HBox(0,0);
                    $container->pack_start($marginBox, 0, 0, $self->{halfMargin});
                }
                $container->pack_start($widget, $expand, $expand, 0)
                    if ref($widget) ne 'HASH';
            }
        }
    }
    
    sub setWidgetStyle
    {
        my ($self, $widget, $name, $style) = @_;
    }
    
    sub getWidget
    {
        my ($self, $field, $info) = @_;
        return $self->{$field};
    }
    
    sub createLayout
    {
        my ($self, $xml, $container, $parent, $noTab) = @_;
        #
        foreach (@{$xml->{item}})
        {
            my $widget;
            my $box;
            my $isContainer = 0;

            # TODO We should return an error here
            $_->{type} = 'default' if ! exists $_->{type};

            if ($_->{type} eq 'formatted')
            {
                my $info = {};
                $info->{type} = 'short text';
                $info->{value} = $_->{name};
                $widget = $self->createItem($info);
                $self->{$_->{name}} = $widget;
                $self->setAsFormatted($widget, $_->{name}, $_->{value});
                $_->{for} = $widget->{name};
            }
            elsif ($_->{type} eq 'value')
            {
                $widget = $self->getWidget($_->{for}, $_);
                if ($widget)
                {
                    $widget->setWidth($_->{width}) if $_->{width};
                    $widget->setHeight($_->{height}) if $_->{height};
                    $widget->expand if (exists $_->{expand}) && ($_->{expand} eq 'true');
                    if ($_->{values} || $self->{model}->{fieldsInfo}->{$_->{for}}->{values})
                    {
                        $self->{displayedValues}->{$_->{for}} = 
                                $_->{values}
                             || $self->{model}->{fieldsInfo}->{$_->{for}}->{values};
                    }
                }
            }
            elsif ($_->{type} eq 'label')
            {
                if ($_->{for})
                { 
                    $widget = $self->{$_->{for}.'Label'};
                }
                else
                {
                    $widget = $self->createLabel($_, $self->{withSeparator});
                }
                if ((exists $_->{align}) && ($_->{align} eq 'center'))
                {
                    $widget->set_alignment(0.5, 0.5);
                    if ($self->{withSeparator})
                    {
                        my $label = $widget->get_label;
                        my $sep = $self->{parent}->{lang}->{Separator};
                        $label =~ s/$sep$//;
                        $widget->set_label($label);
                    }
                }
                $widget->set_alignment(0, 0) if (exists $_->{align}) && ($_->{align} eq 'top');
            }
            elsif ($_->{type} eq 'special')
            {
                $widget = $self->{$_->{for}};
                $widget->{param} = $_->{param};
            }
            elsif ($_->{type} eq 'launcher')
            {
                $widget = Gtk2::Button->new_from_stock('gtk-media-play');
                my $valueWidget = $self->{$_->{for}};
                my $format = $self->{model}->{fieldsInfo}->{$_->{for}}->{format};
                $widget->signal_connect('clicked' => sub {
                    $self->{parent}->launch($valueWidget->getValue, $format);
                });
                push @{$self->{dependencies}->{$_->{for}}}, $widget;
            }
            elsif ($_->{type} eq 'extractor')
            {
                $widget = Gtk2::Button->new($self->{parent}->{lang}->{ExtractButton});
                my $valueWidget = $self->{$_->{for}};
                $widget->signal_connect('clicked' => sub {
                    $self->{parent}->extractInfo($valueWidget->getValue, $self);
                });
                push @{$self->{dependencies}->{$_->{for}}}, $widget;
            }
            elsif ($_->{type} eq 'line')
            {
                my $homogeneous = (exists $_->{homogeneous} && ($_->{homogeneous} eq 'true'))
                                ? 1 : 0;
                $widget = new Gtk2::HBox($homogeneous, $self->{margin});
                #$widget->set_border_width($self->{halfMargin});
                $widget->set_size_request(-1, $_->{height}) if $_->{height};
                $box = $widget;
                $isContainer = 1;
            }
            elsif ($_->{type} eq 'box')
            {
                $widget = new Gtk2::VBox(0,0);
                #$widget->set_border_width($self->{halfMargin});
                $box = $widget;
                if ($_->{width})
                {
#                    $widget = new Gtk2::EventBox;
#                    $widget->add($box);
                    $widget->set_size_request($_->{width}, -1);
                }
                $isContainer = 1;
            }
            elsif ($_->{type} eq 'notebook')
            {
                if ($noTab)
                {
                    $widget = new Gtk2::VBox(0,0);
                }
                else
                {
                    $widget = new Gtk2::Notebook;
                }
                #$widget->set_border_width($self->{margin});
                $box = $widget;
                $isContainer = 1;
            }
            elsif ($_->{type} eq 'tab')
            {
                $widget = new Gtk2::VBox(0,0);
                if ($noTab)
                {
                    my $label = new Gtk2::Label;
                    $label->set_markup('<b>'.$self->{model}->getDisplayedText($_->{title}).'</b>');
                    $label->set_alignment(0,0);
                    $widget->pack_start($label, 0, 0, 0);
                    $widget->set_border_width(0);
                    $box = new Gtk2::VBox(0,0);
                    $box->set_border_width($self->{margin});
                    $widget->pack_start($box, 1, 1, 0);
                }
                else
                {
                    $widget->set_border_width($self->{margin});
                    $box = $widget;
                }
                $isContainer = 1;
            }
            elsif ($_->{type} eq 'table')
            {
                $widget = new Gtk2::Table($_->{rows},$_->{cols});
                #$widget->set_border_width($self->{margin});
                $widget->set_col_spacings($self->{colSpacing});
                $widget->set_row_spacings($self->{rowSpacing});
                $box = $widget;
                $isContainer = 1;
            }
            elsif ($_->{type} eq 'frame')
            {
                $widget = new GCGroup($self->{model}->getDisplayedText($_->{title}));
                $widget->set_border_width(0);
                $widget->setPadding(0);
                $box = new Gtk2::Table($_->{rows},$_->{cols});
                $box->set_border_width($self->{halfMargin});
                $box->set_col_spacings($self->{margin});
                $box->set_row_spacings($self->{halfMargin});
                $widget->addWidget($box);
                $isContainer = 1;
            }
            elsif ($_->{type} eq 'expander')
            {
                #$widget = new GCExpander($self->{model}->getDisplayedText($_->{title}));
                $widget = $self->createExpander($self->{model}->getDisplayedText($_->{title}));
                $widget->setMode($self->{parent}->{options}->expandersMode);
                $widget->{originalLabel} = $self->{model}->getDisplayedText($_->{title});
                $widget->{collapsedTemplate} = $_->{collapsed};
                $self->setExpanderLabel($widget, 1);
                $widget->signal_connect('activate' => sub {
                    $self->setExpanderLabel($widget, 0);
                });
                $widget->set_expanded(1) if (defined $_->{expanded}) && ($_->{expanded} eq 'true');
                push @{$self->{expanders}}, $widget;
                $box = new Gtk2::VBox(0,0);
                $box->set_border_width($self->{halfMargin});
                $widget->add($box);
                $isContainer = 1;
                $_->{style} = 'expander' if !exists $_->{style};
            }
            
            if (exists $_->{border})
            {
                $widget->set_border_width($_->{border});
            }
            
            if ($isContainer)
            {
                $self->createLayout($_, $box, $widget, $noTab);
            }
            push @{$self->{toDestroy}}, $widget;
            $widget->{realParent} = $parent if $parent;
            $self->setWidgetStyle($widget, $_->{for}, $_->{style}) if $_->{style};
            $self->addToContainer($container, $widget, $_) if $widget;
        }
    }
}

{
    package GCReadOnlyPanel;
    use base 'GCItemPanel';

    use File::Basename;

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

        bless ($self, $class);

        $self->{withSeparator} = 0;
        $self->{colSpacing} = $self->{quarterMargin};
        $self->{rowSpacing} = $self->{quarterMargin};
        $self->loadStyles($options->panelStyle);

        $self->{inBox} = new Gtk2::VBox(0, 0);
        $self->{inBox}->set_border_width($self->{margin});
       
        $self->set_name('GCItemFrame');

        $self->{inBox} = new Gtk2::VBox(0, 0);
        my $hboxMargins = new Gtk2::HBox(0,0);
        $hboxMargins->pack_start($self->{inBox}, 1, 1, 50);
        $self->{mainBox} = new Gtk2::EventBox;
        $self->{mainBox}->modify_bg('normal', $self->{styles}->{page}->{bgColor});
        $self->{mainBox}->add($hboxMargins);

        my $realMain = new Gtk2::VBox(0,0);

#        GCUtils::setWidgetPixmap($self->{mainBox},$ENV{GCS_SHARE_DIR}.'/logos/splash.png');

#        $self->{backgroundPixbuf} = Gtk2::Gdk::Pixbuf->new_from_file($ENV{GCS_SHARE_DIR}.'/logos/splash.png');
#        $self->{mainBox}->signal_connect('expose-event' => sub {
#            $self->paintBg;
#        });

        $realMain->pack_start($self->{mainBox},1,1,0);

        $self->{layoutBox} = $self->{inBox};

        $self->add($realMain);
        $self->show_all;


        $self->{warning} = new Gtk2::Label;
        
        $self->{infoBox}->pack_start($self->{warning},1,1,0);
        $self->{infoBox}->pack_start($self->{viewAllBox},1,1,0);
        $realMain->pack_start($self->{infoBox},1,1,0);

        return $self;
    }
    
    sub loadStyles
    {
        my ($self, $style) = @_;
        
        $self->{styles} = {};
        my $styleFile = $ENV{GCS_SHARE_DIR}.'/panels/'.$style;
        
        open STYLE, $styleFile;
        while (<STYLE>)
        {
            chomp;
            next if !$_;
            m/^(.*?)\s*=\s*(.*)$/;
            my $item = $1;
            (my $value = $2) =~ s/^"(.*?)"$/$1/;
            $item =~ /(.*?)(Bg|Color|Style|Justify|Arrow|Prelight)/;
            my $comp = $1;
            my $style = lc $2;
            $self->{styles}->{$comp}->{$style} = $value;
        }            
        close STYLE;
        foreach (keys %{$self->{styles}})
        {
            $self->{styles}->{$_}->{bgColor} = Gtk2::Gdk::Color->parse($self->{styles}->{$_}->{bg});
            $self->{styles}->{$_}->{style} = $self->{styles}->{$_}->{style}
                                           . " foreground='".$self->{styles}->{$_}->{color}."'";
        }
        #$self->{styles}->{invisible}->{}
    }
    
    sub deactivate
    {
        my $self = shift;
    }

    sub getValues
    {
        my ($self, $field) = @_;
        if ($field eq $self->{model}->{commonFields}->{borrower}->{name})
        {
            my $borrowers = $self->{model}->getValues($self->{model}->{commonFields}->{borrower}->{name});
            shift @{$borrowers} if !$borrowers->[0]->{displayed};
            return $borrowers;
        }
        else
        {        
            return $self->{$field}->getValues;                
        }
    }
    

    sub selectTitle
    {
        my $self = shift;
        $self->myRealize;
    }

#    sub paintBg
#    {
#        my $self = shift;
#        my $window = $self->{mainBox}->window;
#        $self->{mainBox}->set_app_paintable(1);
#        if (! $self->{gcMain})
#        {
#            $self->{gcMain} = Gtk2::Gdk::GC->new($window);
#        }
#        $window->draw_pixbuf($self->{gcMain}, $self->{backgroundPixbuf}, 0, 0, 0, 0, -1, -1, "none", 5, 5); 
#    }

    sub setBorrowers
    {
        my ($self) = @_;
    }

    sub createSpecial
    {
        my $self = shift;
    }
    
    sub createLabel
    {
        my ($self, $info) = @_;
        
        my $label = $self->{model}->getDisplayedText($info->{label});
        my $widget = new GCColorLabel($self->{styles}->{label}->{bgColor});
        $widget->set_selectable(0);
        $widget->set_padding(5,5);
        $widget->set_justify('fill');
        $widget->set_markup("<span ".$self->{styles}->{label}->{style}.">".$label.'</span>');
        return $widget;
    }
    
    sub createExpander
    {
        my ($self, $label) = @_;

        return new GCColorExpander($label,
                                   $self->{styles}->{expander}->{bgColor},
                                   $self->{styles}->{expander}->{style});
    }

    sub setWidgetStyle
    {
        my ($self, $widget, $name, $style) = @_;
        $self->{style}->{$name} = $style;
        if ($self->{styles}->{$style}->{bgColor})
        {
            $widget->modify_bg('normal', $self->{styles}->{$style}->{bgColor});
        }

        if ($widget->isa('GCColorLabel') || $widget->isa('GCColorText'))
        {
            my $justify = $self->{styles}->{$style}->{justify};
            $widget->set_justify($justify) if $justify;
        }
        
        if ($widget->isa('GCColorExpander'))
        {
            my $arrowColor = Gtk2::Gdk::Color->parse($self->{styles}->{expander}->{arrow});
            my $prelightColor;
            if (exists $self->{styles}->{expander}->{prelight})
            {
                $prelightColor = Gtk2::Gdk::Color->parse($self->{styles}->{expander}->{prelight});
            }
            else
            {
                $prelightColor = $self->{styles}->{expander}->{bgColor};
            }
            
            $widget->modify_bg('prelight', $prelightColor);
            $widget->get_label_widget->modify_bg('prelight', $prelightColor);
            $widget->modify_fg('normal', $arrowColor);
            $widget->modify_fg('prelight', $arrowColor);
        }
    }
    
    sub createItem
    {
        my ($self, $info) = @_;
        
        my $widget;
        
        if ($info->{type} eq 'long text')
        {
            $widget = new GCColorText($self->{styles}->{field}->{bgColor}, 0);
            $self->{style}->{$info->{value}} = 'field';
        }
        elsif ($info->{type} eq 'image')
        {
            $widget = new GCItemImage($self->{parent}->{options}, 
                                      $self->{parent});
        }
        elsif ($info->{type} eq 'button')
        {
            $widget = new GCButton($self->{model}->getDisplayedText($info->{label}));
        }
        elsif($info->{type} =~ /list$/o)
        {
            my $number = GCUtils::listNameToNumber($info->{type});
            my @labels;
            for my $i (1..$number)
            {
                push @labels, $self->{model}->getDisplayedText($info->{'label'.$i});
            }
            $widget = new GCColorTable($number, \@labels,
                                       $self->{styles}->{label},
                                       $self->{styles}->{field},
                                       $self->{model}->getDisplayedText($info->{label}));            
        }
        elsif ($info->{type} eq 'url')
        {
            $widget = new GCColorLink($self->{styles}->{field}->{bgColor}, $self->{parent});
            $self->{style}->{$info->{value}} = 'field';
            $widget->set_selectable(0);
        }
        else
        {
            # See GCColorLabel constructor for the meaning of listType;
            $widget = new GCColorLabel($self->{styles}->{field}->{bgColor});
            if ($info->{value} eq $self->{model}->{commonFields}->{borrower}->{name})
            {
                $self->{prepare}->{$info->{value}}->{func} = 'prepareBorrower';
            }
            elsif ($info->{type} eq 'date')
            {
                $self->{prepare}->{$info->{value}}->{func} = 'prepareDate';
            }
            $self->{style}->{$info->{value}} = 'field';
            $widget->set_selectable(0);
            $widget->set_padding(5,5);
            $widget->set_justify('fill');
            $self->setAsFormatted($widget, $info->{value}, $info->{init})
                if ($info->{type} eq 'formatted');
        }
        $widget->{name} = $info->{value};        
        return $widget;
    }

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

        my $widget = $self->{$field};
        if ($info->{height} && $widget->isa('GCColorLabel'))
        {
            my $style = $info->{style};
            $style = 'field' if !$style;
            $widget->destroy;
            $widget = new GCColorText($self->{styles}->{$style}->{bgColor}, 0);
            $widget->{name} = $self->{$field}->{name};
            $self->{$field} = $widget;
        }
        elsif ($widget->isa('GCColorTable') && ($info->{flat} eq 'true'))
        {
            my $columns = $widget->{number};
            $widget->destroy;
            $self->{prepare}->{$field}->{func} = 'prepareMultipleList';
            $self->{prepare}->{$field}->{extra} = $columns;
            if ($info->{height})
            {
                $widget = new GCColorText($self->{styles}->{field}->{bgColor}, 0, $columns);
            }
            else
            {
                $widget = new GCColorLabel($self->{styles}->{field}->{bgColor}, $columns);
                $widget->set_selectable(0);
                $widget->set_padding(5,5);
                $widget->set_justify('fill');
            }
            $self->{style}->{$field} = 'field';
            $self->{$field} = $widget;
        }

        return $widget;
    }

    sub prepareMultipleList
    {
        my ($self, $value, $extra) = @_;
        return GCPreProcess::multipleList($value, $extra);
    }
    
    sub prepareBorrower
    {
        my ($self, $value) = @_;

        $value = $self->{lang}->{PanelNobody} if $value eq 'none';
        $value = $self->{lang}->{PanelUnknown} if $value eq 'unknown';
        return $value;
    }

    sub AUTOLOAD
    {
        return if our $AUTOLOAD =~ /::DESTROY$/;
        (my $name = $AUTOLOAD) =~ s/.*?::(.*)/$1/;
        return if !$name;
        no strict 'refs';
        
        *{$AUTOLOAD} = sub {
            my $self = shift;
            return 0 if !$self->{$name};
            return $self->{$name.'Value'}
                if !(scalar @_);
            my $text = shift;
            $self->{$name.'Value'} = $text;
            if (my $func = $self->{prepare}->{$name}->{func})
            {
                $text = $self->$func($text, $self->{prepare}->{$name}->{extra});
            }
            $text = ' ' if ! defined $text;
            $text = $self->{model}->getDisplayedValue($self->{displayedValues}->{$name}, $text)
                if ($self->{displayedValues}->{$name});
            if ($self->{$name})
            {
                if ($self->{$name}->acceptMarkup)
                {
                    $self->{$name}->setMarkup("<span ".
                                              $self->{styles}->{$self->{style}->{$name}}->{style}.
                                              ">".$self->{$name}->cleanMarkup($text).'</span>');
                }
                else
                {
                    $self->{$name}->setValue($text);
                }
                #$self->{$name}->resetChanged;
            }
        };
        goto &{$AUTOLOAD};
    }
}


{
    package GCFormPanel;

    use base 'GCItemPanel';

    sub createSpecial
    {
        my $self = shift;
    
        $self->{mailButton} = new GCButton($self->{parent}->{lang}->{MailTitle});
        $self->{mailButton}->signal_connect('clicked' => sub {
            $self->sendBorrowerEmail;
        });
        push @{$self->{toDestroy}}, $self->{mailButton};

        $self->{itemBackButton} = new GCButton($self->{parent}->{lang}->{PanelReturned});
        $self->{itemBackButton}->signal_connect('clicked' => sub {
            $self->itemBack;
        });
        push @{$self->{toDestroy}}, $self->{mailButton};

        $self->{searchButton} = GCButton->newFromStock('gtk-jump-to');
        $self->{searchButton}->signal_connect('activate' => sub {
            $self->searchItem;
        });
        $self->{searchButton}->signal_connect('button_release_event' => sub {
            my ($widget, $event) = @_;
            $self->searchItem($event->button == 3);
        });
        $self->{tooltips}->set_tip($self->{searchButton}, $self->{parent}->{lang}->{PanelSearchTip}, '');
        push @{$self->{toDestroy}}, $self->{searchButton};
        
        $self->{deleteButton} = GCButton->newFromStock('gtk-delete');
        $self->{deleteButton}->signal_connect("clicked" => \&deleteItem, $self);
        $self->{tooltips}->set_tip($self->{deleteButton}, $self->{parent}->{lang}->{PanelRemoveTip}, '');
        push @{$self->{toDestroy}}, $self->{deleteButton};
    }

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

        bless ($self, $class);

        $self->{withSeparator} = 1;
        $self->{locked} = 0;
        $self->{colSpacing} = $self->{margin};
        $self->{rowSpacing} = $self->{halfMargin};

        $self->{mainBox} = new Gtk2::VBox(0, 0);
        $self->{mainBox}->set_border_width($self->{margin});

        $self->set_name('GCItemFrame');

        my $realMain = new Gtk2::VBox(0,0);

        $realMain->pack_start($self->{mainBox},1,1,0);
        $self->{layoutBox} = $self->{mainBox};

        $self->add($realMain);
        $self->show_all;

        $self->{warning} = new Gtk2::Label;
        
        $self->{infoBox}->pack_start($self->{warning},1,1,0);
        $self->{infoBox}->pack_start($self->{viewAllBox},1,1,0);
        $realMain->pack_start($self->{infoBox},1,1,0);
        
        return $self;
    }
    
    sub isReadOnly
    {
        return 0;
    }

    sub searchItem
    {
        my ($self, $forceSelection) = @_;

        #$self->{parent}->searchItemForPanel($self->{$self->{model}->{commonFields}->{title}}->getValue, $self);
        $self->{parent}->searchItemForPanel($self, $forceSelection);

        $self->showMe;
    }

    sub checkBorrowerButtons
    {
        my $self = shift;
        my $borrowerField = $self->{model}->{commonFields}->{borrower}->{name};
        return if ! $self->{$borrowerField};
        my $locked = (($self->{$borrowerField}->getValue eq 'none')
                   || ($self->{$borrowerField}->getValue eq 'unknown'));
        $self->{mailButton}->lock($locked)
            if $self->{mailButton};
        $locked = ($self->{locked} || ($self->{$borrowerField}->getValue eq 'none'));
        $self->{itemBackButton}->lock($locked)
            if $self->{itemBackButton};
    }
    
    sub itemBack
    {
        my $self = shift;

        my $isBack = 0;
        my $dialog = new GCDateSelectionDialog($self->{parent}, $self->{lang}->{PanelReturned});
        if ($dialog->show)
        {
            my $borrowerField = $self->{model}->{commonFields}->{borrower}->{name};
            my $lendDateField = $self->{model}->{commonFields}->{borrower}->{date};
            my $borrowingsField = $self->{model}->{commonFields}->{borrower}->{history};
            my $coverField = $self->{model}->{commonFields}->{cover};
            my @data = ($self->{$borrowerField}->getDisplayedValue,
                        $self->{$lendDateField}->getValue,
                        $dialog->date);
            foreach (@data[1..2])
            {
                $_ = $self->prepareDate($_);
            }
            $self->{$borrowingsField}->addValues(@data);
            my $previous = $self->getAsHash;
            $self->{$borrowerField}->setValue('none');
            $self->{$lendDateField}->clear;
            my $new = $self->getAsHash;
            $self->{parent}->{itemsView}->changeCurrent($previous, $new, $self->{parent}->{items}->getCurrent, 1)
                if $self->$coverField;
            $isBack = 1;
        }
        $self->showMe;
        return $isBack;
    }
    
    sub lock
    {
        my ($self, $value) = @_;
        $self->changeState($self, $value);
    }
    
    sub deactivate
    {
        my $self = shift;
        $self->changeState($self,1);
    }

    sub changeState
    {
        my ($caller, $self, $locked)  = @_;
        $locked = 1 if $self->{parent}->{items}->getLock;
        return if $self->{locked} == $locked;
        $self->{locked} = $locked;
        foreach (@{$self->{parent}->{model}->{fieldsNames}})
        {
            $self->{$_}->lock($locked) if ($self->{$_});
        }

        $self->{searchButton}->lock($locked);
        $self->checkBorrowerButtons;
        $self->{deleteButton}->lock($locked);
    }
    
    sub setShowOptionForSpecials
    {
        my ($self, $show, $hasToShow) = @_;

        if (! $show->{$self->{model}->{commonFields}->{borrower}->{name}})
        {
            $self->{mailButton}->hide;
            $self->{itemBackButton}->hide;
        }
   }

    sub setBorrowers
    {
        my $self = shift;

        return if ! $self->{borrowerControl};
  
        my $borrowers = $self->{model}->getValues($self->{model}->{commonFields}->{borrower}->{name});
        shift @{$borrowers} if !$borrowers->[0]->{displayed};
        $self->{borrowerControl}->setValues($borrowers);

        return;
    }

    sub sendBorrowerEmail
    {
        use GCDialogs;

        my $self = shift;

        my %info;
        $info{title} = $self->{$self->{model}->{commonFields}->{title}}->getValue;
        $info{borrower} = $self->{$self->{model}->{commonFields}->{borrower}->{name}}->getValue;
        $info{lendDate} = $self->prepareDate(
            $self->{$self->{model}->{commonFields}->{borrower}->{date}}->getValue
        );
        $self->{parent}->sendBorrowerEmail(\%info);
    }

    sub selectTitle
    {
        my $self = shift;
        my $titleField = $self->{model}->{commonFields}->{title};
        #$self->{$titleField}->select_region(0, length($self->{$titleField}->getValue));
        $self->{$titleField}->selectAll;
    }

    sub deleteItem
    {
       my ($widget, $self) = @_;

       $self->{parent}->deleteCurrentItem;
    }

    sub createItem
    {
        my ($self, $info) = @_;
        
        my $widget;
        
        if (exists $info->{linkedto})
        {
            $widget = new GCLinkedComponent($self->{$info->{linkedto}});
        }
        elsif ($info->{type} eq 'short text')
        {
            $widget = new GCShortText;
            $widget->signal_connect('activate', sub {
                $self->searchItem;
            }) if $self->{model}->isSearchField($info->{value});
        }
        elsif ($info->{type} eq 'long text')
        {
            $widget = new GCLongText;
            $widget->setSpellChecking($self->{parent}->{options}->spellCheck);
            push @{$self->{longTextFields}}, $widget;
        }
        elsif ($info->{type} eq 'history text')
        {
            $widget = new GCHistoryText;
        }
        elsif ($info->{type} eq 'options')
        {
            $info->{values} = $info->{value} if !$info->{values};
            if ($info->{value} eq $self->{model}->{commonFields}->{borrower}->{name})
            {
                $widget = new GCMenuList;
                #When borrower not found, we use the last choice: unknown
                $widget->setLastForDefault;
                $self->{borrowerControl} = $widget;
                $widget->signal_connect('changed' => sub {
                    $self->checkBorrowerButtons;
                });
                $self->setBorrowers;
            }
            else
            {
                $widget = new GCMenuList($self->{model}->getValues($info->{values}), $info->{separator});
            }
        }
        elsif ($info->{type} =~ /list$/o)
        {
            my $number;
            my $readonly = 0;
            my $withHistory = 1;
            my @labels;
            $withHistory = 0
                if (exists $info->{history}) && ($info->{history} eq 'false');
            $readonly = 1
                if $info->{value} eq $self->{model}->{commonFields}->{borrower}->{history};
            $number = GCUtils::listNameToNumber($info->{type});
            if ($number == 1)
            {
                @labels = ($self->{model}->getDisplayedText($info->{label}));
            }
            else
            {
                for my $i (1..$number)
                {
                    push @labels, $self->{model}->getDisplayedText($info->{'label'.$i});
                }
            }
            $widget = new GCMultipleList($self, $number, \@labels, $withHistory, $readonly);
        }
		elsif ($info->{type} eq 'checked text')
		{
			$widget = new GCCheckedText($info->{format});
			$widget->signal_connect('activate', sub {
				$self->searchItem;
			}) if $self->{model}->isSearchField($info->{value});
		}
        elsif ($info->{type} eq 'number')
        {
            if (($info->{displayas} eq 'graphical')
             && ($self->{parent}->{options}->useStars))
            {
                $widget = new GCNumeric($info->{init}, 0, $info->{max}, 0, 'graphical');
            }
            else
            {
                if ((exists $info->{min}) && ($info->{min} ne ''))
                {
                    $widget = new GCNumeric($info->{init}, 
                                            $info->{min},
                                            $info->{max},
                                            $info->{step},
                                            'text');
                }
                else
                {
                    $widget = new GCCheckedText('0-9.');
                    $widget->setReadOnly
                        if ($info->{value} eq $self->{model}->{commonFields}->{id});
                }
                $widget->signal_connect('activate', sub {
                    $self->searchItem;
                }) if $self->{model}->isSearchField($info->{value});
            }
        }
        elsif ($info->{type} eq 'image')
        {
            my $img = new GCItemImage($self->{parent}->{options}, 
                                      $self->{parent});
            my $isCover = 0;
            $isCover = 1 if $info->{value} eq $self->{model}->{commonFields}->{cover};
            #$img->setImmediate if $isCover;
            $widget = new GCImageButton($self, $img, $isCover, $info->{default});
        }
        elsif ($info->{type} eq 'button')
        {
            if ($info->{format} eq 'url')
            {
                $widget = new GCUrlButton($self->{model}->getDisplayedText($info->{label}),
                                          $self->{parent});
                $info->{tip} = $self->{lang}->{PanelWebTip}
                    if $info->{value} eq $self->{model}->{commonFields}->{url};
            }
            else
            {
                $widget = new GCButton($self->{model}->getDisplayedText($info->{label}));
            }
        }
        elsif ($info->{type} eq 'url')
        {
            $widget = new GCUrl($self->{parent});
        }
        elsif ($info->{type} eq 'yesno')
        {
            $widget = new GCCheckBox($self->{model}->getDisplayedText($info->{label}));
        }
        elsif ($info->{type} eq 'file')
        {
            $widget = new GCFile($self);
        }
        elsif ($info->{type} eq 'date')
        {
            $widget = new GCDate($self, $self->{lang}, 0,
                                 $self->{parent}->{options}->dateFormat);
            push @{$self->{dateFields}}, $widget;
        }
        elsif ($info->{type} eq 'formatted')
        {
            $widget = new GCShortText;
            $widget->setReadOnly;
            $self->setAsFormatted($widget, $info->{value}, $info->{init});
        }
        else
        {
            print "Invalid type : ",$info->{type}," for ",$info->{value},"\n";
            $widget = 0;
        }
        
        $widget->{name} = $info->{value} if $widget;
        $self->{tooltips}->set_tip($widget, $self->{model}->getDisplayedText($info->{tip}), '')
            if $info->{tip};
        $widget->activateStateTracking;
        return $widget;
    }
    
    sub getValues
    {
        my ($self, $field) = @_;
        return $self->{$field}->getValues;
    }
    
    sub AUTOLOAD
    {
        return if our $AUTOLOAD =~ /::DESTROY$/;
        (my $name = $AUTOLOAD) =~ s/.*?::(.*)/$1/;
        return if !$name;
        my $self = $_[0];
        no strict 'refs';
        {
            *{$AUTOLOAD} = sub {
                my $self = shift;
                return 0 if !$self->{$name};
                return $self->{$name}->getValue
                    if !(scalar @_);
                $self->{$name}->setValue(shift);
            };
        }
        goto &{$AUTOLOAD};
    }
}

1;
