diff options
Diffstat (limited to 'lib/gcstar/GCGraphicComponents/GCBaseWidgets.pm')
| -rw-r--r-- | lib/gcstar/GCGraphicComponents/GCBaseWidgets.pm | 4023 | 
1 files changed, 4023 insertions, 0 deletions
diff --git a/lib/gcstar/GCGraphicComponents/GCBaseWidgets.pm b/lib/gcstar/GCGraphicComponents/GCBaseWidgets.pm new file mode 100644 index 0000000..f2f9af4 --- /dev/null +++ b/lib/gcstar/GCGraphicComponents/GCBaseWidgets.pm @@ -0,0 +1,4023 @@ +package GCBaseWidgets; + +################################################### +# +#  Copyright 2005-2010 Christian Jodar +# +#  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +# +################################################### +use utf8; +use Gtk2; +#use GCBorrowings; +use Encode; + + +use strict; + +our @videoExtensions = ('.aaf','.3gp','.asf','.avi','.flv','.m1v','.m2v','.m4v','.mkv','.mov', +                           '.mp4','.mpeg','.mpg','.mpe','.mxf','.nsv','.ogg','.ogv','.rm','.swf','.wmv', +                           '.iso'); +             +our @ebookExtensions = ('.txt','.htm','.html','.azw','.opf','.tr2','.tr3','.aeh','.fb2','.chm', +                           '.pdf','.ps','.djvu','.lit','.pdb','.dnl','.xeb','.ceb','.lbr','.prc', +                           '.mobi','.epub','.lrf','.lrx','.pdg','.doc','.odt','.cbr','.cbz','.djvu'); +                            +our @audioExtensions = ('.m3u','.pls','.asx','.wax','.wvx','.b4s','.kpl','.ram','.smil','.iso', +                            '.cue','.bin','.mp3','.ogg','.oga','.flac'); + +sub createWidget +{ +    my ($parent, $info, $comparison) = @_; +    my $widget; +    my $withComparisonLabel = 1; + +    if ($info->{type} eq 'short text') +    { +        if ($comparison eq 'range') +        { +            $widget = new GCRange('text', $parent->{lang}); +            $widget->setWidth(16); +            $withComparisonLabel = 0; +        } +        else +        { +            $widget = new GCShortText; +        } +    } +    elsif ($info->{type} eq 'number') +    { +        #If we want to have values that are less to the specified one, +        #we use max as default to be sure everything will be returned +        #in that case. +        my $default = $info->{min}; +        $default = $info->{max} +        if $comparison =~ /^l/; +        if (exists $info->{min}) +        { +            if ($comparison eq 'range') +            { +                $widget = new GCRange('number', +                                      $parent->{lang}, +                                      $info->{min}, +                                      $info->{max}, +                                      $info->{step}); +                $widget->setWidth(16); +                $withComparisonLabel = 0; +            } +            else +            { +                $widget = new GCNumeric($default, +                                        $info->{min}, +                                        $info->{max}, +                                        $info->{step}); +            } +        } +        else +        { +            if ($comparison eq 'range') +            { +                $widget = new GCRange('numeric text', $parent->{lang}); +                $widget->setWidth(16); +                $withComparisonLabel = 0; +            } +            else +            { +                $widget = new GCCheckedText('0-9.'); +            } +        } +    } +    elsif ($info->{type} eq 'checked text') +    { +        $widget = new GCCheckedText($info->{format}); +    } +    elsif (($info->{type} eq 'history text') +           || (($info->{type} =~ /list/) +               && ($info->{history} ne 'false'))) +    { +        $widget = new GCHistoryText; +    } +    elsif ($info->{type} eq 'options') +    { +        $widget = new GCMenuList; +        $widget->setValues($parent->{model}->getValues($info->{values}), $info->{separator}); +    } +    elsif ($info->{type} eq 'yesno') +    { +        $widget = new GCCheckBoxWithIgnore($parent); +        $withComparisonLabel = 0; +    } +    elsif ($info->{type} eq 'date') +    { +    	if ($comparison eq 'range') +        { +            $widget = new GCRange('date', $parent->{lang}, undef, undef, undef, $parent); +            $widget->setWidth(16); +            $withComparisonLabel = 0; +        } +        else +        { +			$widget = new GCDate($parent->{window}, $parent->{lang}, 1, +            	                 $parent->{options}->dateFormat); +        } +    } +    else +    { +        $widget = new GCShortText; +    } +     +    return ($widget, $withComparisonLabel); +} + +{ +    package GCGraphicComponent; + +    use base 'Exporter'; +    our @EXPORT = qw($somethingChanged); +    our $somethingChanged = 0; + +    sub expand +    { +    } +     +    sub lock +    { +    } + +    sub getMainParent +    { +        my $self = shift; +        return if ! $self->{parent}; +        my $tmpWidget = $self; +        $tmpWidget = $tmpWidget->{parent} while $tmpWidget && (! $tmpWidget->isa('GCFrame')); +        $self->{mainParent} = $tmpWidget; +    } +     +    sub acceptMarkup +    { +        my $self = shift; +        return 0; +    } + +    sub cleanMarkup +    { +        my ($self, $text, $encodeSubset) = @_; +        $text =~ s|<br ?/?>|\n|g; +        if ($encodeSubset) +        { +            # Encode only the characters set_markup has issues with +            $text =~ s|&|&|g;                    +            $text =~ s|<|<|g;        +            $text =~ s|>|>|g;        +        } +        else +        { +            $text = GCUtils::encodeEntities($text) +        } +        return $text; +    } + +    sub selectAll +    { +    } + +    sub getTagFromSpan +    { +        my ($self, $desc) = @_; +        my @result = (); #('background' => $self->{background}); +        my @keyvalues = split / /, $desc; +        foreach (@keyvalues) +        { +            /([^=]*)=(.*)/; +            my $key = $1; +            my $value = $2; +            $value =~ s/('|")//g; +            #"' +            next if $key =~ /=/; +            push @result, ($key, $value); +        } +        return @result; +    } +     +    sub valueToDisplayed +    { +        # 1st parameter is self +        shift; +        # Here we don't change the value; +        return shift; +    } +     +    sub hasChanged +    { +        my $self = shift; + +        return $self->{hasChanged}; +    } +     +    sub setChanged +    { +        my $self = shift; +        $self->{hasChanged} = 1; +        $somethingChanged = 1; +    } + +    sub setWidth +    { +        my ($self, $value) = @_; +    } + +    sub setHeight +    { +        my ($self, $height) = @_; +    } + +    sub resetChanged +    { +        my $self = shift; +        $self->{hasChanged} = 0; +    } + +    sub activateStateTracking +    { +        # We do nothing by default +        # Other widget should connect a signal handler or something similar +        # when the content has been changed +    } +     +    sub getLinkedValue +    { +    } +    sub setLinkedValue +    { +    } + +    sub setLinkedComponent +    { +        my ($self, $linked) = @_; +        $self->{linkedComponent} = $linked; +    } +} + +{ +    package GCPseudoHistoryComponent; +    # +    # This is an abstract package handling a little history +    # +    sub initHistory +    { +        my ($self, $listType) = @_; +        $self->{history} = {}; + +        # listType contains the type of the original field: +        #   0: No list +        #   >0: Multiple list (number of columns) +        $self->{listType} = $listType; +         +        $self->{history}->{0} = {'' => 1}; +        for(my $i = 1; $i < $listType; $i++) +        { +            $self->{history}->{$i} = {'' => 1}; +        } +        $self->{listType} = $listType; +    } + +    sub addHistory +    { +        my $self = shift; + +        my $value = shift; +        my $i; +        if (ref($value) eq 'ARRAY') +        { +            foreach (@$value) +            { +                $i = 0; +                foreach my $item(@$_) +                { +                    $self->{history}->{$i}->{$item} = 1; +                    $i++; +                } +            } +        } +        else +        { +            # The separator used was ; instead of , +            $value =~ s/;/,/g if $value !~ /,/; +            my @values = split m/,/, $value; +            foreach (@values) +            { +                my @items = split m/;/; +                $i = 0; +                foreach my $item(@items) +                { +                   $self->{history}->{$i}->{$item} = 1; +                   $i++; +                } +            } +        } +    } + +    sub setDropDown +    { +    } + +    sub getValues +    { +        my $self = shift; +        my @array; +         +         +        if ($self->{listType} < 1) +        { +            @array = sort keys %{$self->{history}->{0}}; +        } +        else +        { +            foreach (sort keys %{$self->{history}}) +            { +                my @tmpArray = sort keys %{$self->{history}->{$_}}; +                push @array, \@tmpArray; +            } +        } +        return \@array; +    } + +    sub setValues +    { +        my ($self, $values) = @_; +        if ($self->{listType} == 0) +        { +            $self->{history} = {}; +            $self->{history}->{0}->{$_} = 1 foreach (@$values); +        } +        else +        { +            $self->{history} = {}; +            for (my $i = 0; $i < $self->{listType}; $i++) +            { +                $self->{history}->{$i}->{$_} = 1 foreach (@{$values->[$i]}); +            } +        } +    } +} + +{ +    package GCLinkedComponent; +    @GCLinkedComponent::ISA = ('GCGraphicComponent'); + +    sub new +    { +        my ($proto, $linked) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = {linked => $linked}; +        bless ($self, $class); +        $linked->setLinkedComponent($self); +        return $self; +    } + +    sub setValue +    { +        my $self = shift; +        $self->setChanged; +        return $self->{linked}->setLinkedValue(@_); +    } + +    sub getValue +    { +        my $self = shift; +        return $self->{linked}->getLinkedValue(@_); +    } + +    sub resetChanged +    { +        my $self = shift; +        $self->{hasChanged} = 0; +        return $self->{linked}->resetChanged(@_); +    } +     +    sub hide +    { +        my $self = shift; +        $self->{linked}->setLinkedActivated(0); +    } + +    sub show +    { +        my $self = shift; +        $self->{linked}->setLinkedActivated(1); +    } +} + +{ +    package GCShortText; + +    use Glib::Object::Subclass +                Gtk2::Entry:: +    ; +     +    @GCShortText::ISA = ('Gtk2::Entry', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; + +        bless ($self, $class); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        return if $self->{readOnly}; +        $self->signal_connect('changed' => sub {; +            $self->setChanged; +        }); +    } +     +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub selectAll +    { +        my $self = shift; +        $self->select_region(0, length($self->getValue)); +        $self->grab_focus; +    } +     +    sub getValue +    { +        my $self = shift; +        return $self->get_text; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; +        $self->set_text($value); +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } +     +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->set_width_chars($value); +    } +     +    sub setReadOnly +    { +        my $self = shift; +        $self->set_editable(0); +        $self->{readOnly} = 1; +    } + +    sub lock +    { +        my ($self, $locked) = @_; +        return if $self->{readOnly}; +        #$self->can_focus(!$locked); +        $self->set_editable(!$locked); +    } +} + +our $hasSpellChecker; +BEGIN { +    eval 'use Gtk2::Spell'; +    if ($@) +    { +        $hasSpellChecker = 0; +    } +    else +    { +        $hasSpellChecker = 1; +    } +} +{ +    package GCLongText; +     + +    use Glib::Object::Subclass +                Gtk2::ScrolledWindow:: +    ; +     +    @GCLongText::ISA = ('Gtk2::ScrolledWindow', 'GCGraphicComponent'); + +    sub new +    { +        my ($proto) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +     +        $self->{text} = new Gtk2::TextView; +        $self->{text}->set_editable(1); +        $self->{text}->set_wrap_mode('word'); +        $self->set_border_width(0); +        $self->set_shadow_type('in'); +        $self->set_policy('automatic', 'automatic'); +        #$self->set_size_request(-1,80); +         +        $self->add($self->{text}); +        $self->{spellChecker} = 0; + +        bless ($self, $class); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->{text}->get_buffer->signal_connect('changed' => sub {; +            $self->setChanged; +        }); +    } +     +    sub setSpellChecking +    { +        my ($self, $activate, $lang) = @_; +        return if ! $GCGraphicComponents::hasSpellChecker; +        if ($activate) +        { +            $lang ||= $ENV{LANG}; +            if ($self->{spellChecker}) +            { +                return if $lang eq $self->{lang}; +                $self->setSpellChecking(0) +            } +            eval { +                $self->{spellChecker} = Gtk2::Spell->new_attach($self->{text}); +                $self->{spellChecker}->set_language($lang); +                $self->{lang} = $lang; +            }; +            if ($@) +            { +                $self->setSpellChecking(0); +            } +        } +        else +        { +            $self->{spellChecker}->detach if $self->{spellChecker}; +            $self->{spellChecker} = 0; +        } +    } +     +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub getValue +    { +        my $self = shift; +        my $buffer = $self->{text}->get_buffer; +        my $text = $buffer->get_text($buffer->get_start_iter, +                   $buffer->get_end_iter, 1); +        #$text =~s/\n/<br\/>/g; +        return $text; +    } +     +    sub setValue +    { +        my ($self, $text) = @_; +        #$text =~s/<br\/>/\n/g; +        $text = '' if !defined $text; +        $self->{text}->get_buffer->set_text($text); +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->{text}->can_focus(!$locked); +    } +     +    sub setHeight +    { +        my ($self, $height) = @_; +         +        # TODO Change height +    } +} + +{ +    package GCHistoryText; +     + +    use Glib::Object::Subclass +                Gtk2::Combo:: +    ; +     +    @GCHistoryText::ISA = ('Gtk2::Combo', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +        $self->{history} = {'' => 1}; +         +        # Settings for auto-completion +        $self->{completionModel} = Gtk2::ListStore->new('Glib::String'); +        $self->{completion} = Gtk2::EntryCompletion->new; +        $self->{completion}->set_model($self->{completionModel}); +        $self->{completion}->set_text_column(0); +        $self->entry->set_completion($self->{completion}); + +        bless ($self, $class); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->entry->signal_connect('changed' => sub {; +            $self->setChanged; +        }); +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub selectAll +    { +        my $self = shift; +        $self->entry->select_region(0, -1); +        $self->entry->grab_focus; +    } + +    sub getValue +    { +        my $self = shift; +        my @children = $self->get_children; +        return $children[0]->get_text if $children[0]; +    } +     +    sub setValue +    { +        my ($self, $text) = @_; +        my @children = $self->get_children; +        $children[0]->set_text($text); +    } + +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } +     +    sub setWidth +    { +        my ($self, $value) = @_; +        ($self->get_children)[0]->set_width_chars($value); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +     +        ($self->get_children)[0]->can_focus(!$locked); +        ($self->get_children)[1]->set_sensitive(!$locked); +    } +     +    sub addHistory +    { +        my $self = shift; +        my $value = (scalar @_) ? shift : $self->getValue; +        my $noUpdate = shift; +        $value =~ s/^\s*//; +        if (!exists $self->{history}->{$value}) +        { +            $self->{history}->{$value} = 1; +            if (!$noUpdate) +            { +                $self->setDropDown(sort keys %{$self->{history}}); +            } +        } +    } +     +    sub setDropDown +    { +        my $self = shift; +        my @values = (scalar @_) ? @_ : sort keys %{$self->{history}}; +        my $previousValue = $self->getValue; +         +        # Update history list +        $self->set_popdown_strings(@values); +         +        # Restore value as it is lost when updating list +        $self->setValue($previousValue); +         +        # Update auto-completion list +        $self->{completionModel}->clear; +        foreach (@values) +        { +            my $iter = $self->{completionModel}->append; +            $self->{completionModel}->set($iter, +                                          0 => $_); +        } +    } +     +    sub getValues +    { +        my $self = shift; +        my @array = sort keys %{$self->{history}}; +        return \@array; +    } + +    sub setValues +    { +        my ($self, $values) = @_; +        $self->{history} = {}; +        $self->setDropDown(@$values); +        $self->{history}->{$_} = 1 foreach (@$values); +    } + +    sub setActivate +    { +        my $value +    } + +    sub popup +    { +        my $self = shift; +        ($self->get_children)[1]->grab_focus; +        ($self->get_children)[1]->signal_emit('activate'); +    } +} + +{ +    package GCNumeric; +     + +    use Glib::Object::Subclass +                Gtk2::SpinButton:: +    ; +     +    @GCNumeric::ISA = ('Gtk2::SpinButton', 'GCRatingWidget', 'GCGraphicComponent'); +     +    use GCWidgets; +    use GCLang; + +    sub new +    { +        my ($proto, $default, $min, $max, $step, $format) = @_; +        my $class = ref($proto) || $proto; + +        my $self; + +        if (($format eq 'text') || (!$format)) +        { +            # Standard numeric field +            $step = 1 if !$step; +            my $pageStep = 10 * $step; + +            my $decimals = 0; +            $decimals = length($1) if $step =~ /\.(.*)$/; + +            my $accel = 0; +            my $values = ($max - $min) / $step; +            $accel = 0.2 if $values > 100; +            $accel = 0.5 if $values > 500; +            $accel = 1.0 if $values > 2000; +            $default = 0 if $default eq ''; +            my $adj = Gtk2::Adjustment->new($default, $min, $max, $step, $pageStep, 0) ; +            $self = $class->SUPER::new($adj, $accel, $decimals); +            $self->{default} = $default; +            $self->{step} = $step; +            $self->{pageStep} = $pageStep; +            $self->{accel} = $accel; +            $self->{format} = 'text'; +            $self->set_numeric(1); +        } +        elsif ($format eq 'graphical') +        { +            # Graphical rating widget +            $default = 0 if $default eq ''; +            $max = 10 if $max eq ''; + +            $self = GCRatingWidget->new (maxStars=>$max, rating=>$default, direction=>GCLang::languageDirection($ENV{LANG})); +            $self->{format} = 'graphical'; +        } +        bless ($self, $class); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->signal_connect('changed' => sub {; +            $self->setChanged; +        }); +    } + +    sub isEmpty +    { +        my $self = shift; +         +        if ($self->{format} eq 'text') +        { +            return $self->get_text eq ''; +        } +        elsif ($self->{format} eq 'graphical') +        { +            return $self->get('rating') eq 0; +        } +    } + +    sub selectAll +    { +        my $self = shift; +        if ($self->{format} eq 'text') +        { +            $self->select_region(0, length($self->getValue)); +        } +    } +     +    sub getValue +    { +        my $self = shift; +        my $value; + +        if ($self->{format} eq 'text') +        { +            $value = $self->get_text; +            $value =~ s/,/./; +        } +        elsif ($self->{format} eq 'graphical') +        { +            $value = $self->get('rating'); +        } + +        return $value; +    } + +    sub setValue +    { +        my ($self, $text) = @_; + +        if ($self->{format} eq 'text') +        { +            $text = $self->{default} if $text eq ''; +            $self->set_value($text); +        } +        elsif ($self->{format} eq 'graphical') +        { +            $text = $self->{default} if $text eq ''; +            $self->set_rating($text); +        } +    } +     +    sub clear +    { +        my $self = shift; + +        if ($self->{format} eq 'text') +        { +            $self->set_value($self->{default}); +        } +        elsif ($self->{format} eq 'graphical') +        { +            $self->set_rating($self->{default}); +        } +    } +     +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->set_width_chars($value); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; + +        if ($self->{format} eq 'text') +        { +            $self->can_focus(!$locked); +            my $step = ($locked ? 0 : $self->{step}); +            $self->set_increments($step, $self->{pageStep}); +        } +        elsif ($self->{format} eq 'graphical') +        { +            $self->set(sensitive=>!$locked); +        } +    } +} + +{ +    package GCCheckedText; + +    use Glib::Object::Subclass +                Gtk2::Entry:: +    ; +     +    @GCCheckedText::ISA = ('GCShortText'); +     +    sub new +    { +        my ($proto, $format) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +        bless ($self, $class); +        my $forbidden = qr/[^$format]/; +        $self->signal_connect('insert-text' => sub { +                # Remove forbidden characters +                $_[1] =~ s/$forbidden//g; +                () # this callback must return either 2 or 0 items. +        }); + +        return $self; +    } +} + +{ +    package GCRange; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCRange::ISA = ('Gtk2::HBox', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $type, $lang, $min, $max, $step, $parent) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +        bless ($self, $class); + +        if ($type eq 'text') +        { +            $self->{from} = new GCShortText; +            $self->{to} = new GCShortText; +        } +        elsif ($type eq 'numeric text') +        { +            new GCCheckedText('0-9.'); +            $self->{to} = new GCCheckedText('0-9.'); +        } +        elsif ($type eq 'date') +        { +            $self->{from} = new GCDate($parent->{window}, $lang, 1, +           	                           $parent->{options}->dateFormat); +            $self->{to} = new GCDate($parent->{window}, $lang, 1, +           	                         $parent->{options}->dateFormat); +        } +        else +        { +            $self->{from} = new GCNumeric($min, $min, $max, $step); +            $self->{to} = new GCNumeric($max, $min, $max, $step); +        } +        $self->pack_start(Gtk2::Label->new($lang->{PanelFrom}), 0, 0, 12); +        $self->pack_start($self->{from}, 1, 1, 0); +        $self->pack_start(Gtk2::Label->new($lang->{PanelTo}), 0, 0, 12); +        $self->pack_start($self->{to}, 1, 1, 0); +         +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->{from}->activateStateTracking; +        $self->{to}->activateStateTracking; +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub getValue +    { +        my $self = shift; +        return $self->{from}->getValue.';'.$self->{to}->getValue; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; +        my @values = split m/;/, $value; +        $self->{from}->setValue($values[0]); +        $self->{to}->setValue($values[0]); +    } +     +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->{from}->setWidth($value / 2); +        $self->{to}->setWidth($value / 2); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->{from}->lock(!$locked); +        $self->{to}->lock(!$locked); +    } +     +    sub signal_connect +    { +        my $self = shift; +        $self->{from}->signal_connect(@_); +        $self->{to}->signal_connect(@_); +    } +     +    sub AUTOLOAD +    { +        my $self = shift; +        my $name = our $AUTOLOAD; +        return if $name =~ /::DESTROY$/; +        $name =~ s/.*?::(.*)/$1/; +        $self->{from}->$name(@_); +        $self->{to}->$name(@_); +    } +} + +{ +    package GCDate; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCDate::ISA = ('Gtk2::HBox', 'GCGraphicComponent'); +     +    sub selectValue +    { +        my $self = shift; +        $self->{dialog} = new GCDateSelectionDialog($self->{mainParent}) +            if ! $self->{dialog}; +        $self->{dialog}->date($self->getRawValue); +        if ($self->{dialog}->show) +        { +            $self->setValue($self->{dialog}->date); +        } +        $self->{parent}->showMe; +    } + +    sub new +    { +        my ($proto, $parent, $lang, $reverseDate, $format) = @_; +        $format ||= '%d/%m/%Y'; +        my $class = ref($proto) || $proto; + +        my $self = $class->SUPER::new; +        bless ($self, $class); + +        $self->{parent} = $parent; +        $self->getMainParent; +        $self->{reverseDate} = $reverseDate; +        $self->{entry} = Gtk2::Entry->new; #_with_max_length(10); +        $self->{entry}->set_width_chars(12); +        $self->{button} = new Gtk2::Button($lang->{PanelDateSelect}); +        $self->{button}->signal_connect('clicked' => sub { +            $self->selectValue; +        }); +        $self->pack_start($self->{entry}, 1, 1, 0); +        $self->pack_start($self->{button}, 0, 0, 0); +         +        $self->{format} = $format; +        #$self->{format} = '%d %B %Y'; +        return $self; +    } + +    sub setFormat +    { +        my ($self, $format) = @_; +        my $current = $self->getValue; +        $self->{format} = $format; +        $self->setValue($current); +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->{entry}->signal_connect('changed' => sub {; +            $self->setChanged; +        }); +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub getCurrentDate +    { +        my $self = shift; +         +        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +        return sprintf('%02d/%02d/%4d', $mday, $mon+1, 1900+$year); +    } +     +     +    sub getRawValue +    { +        my $self = shift; +        my $value = $self->{entry}->get_text; +        $value = GCUtils::strToTime($value, $self->{format}) +            if $self->{format} && $value; +        return $value; +         +    } +     +    sub getValue +    { +        my $self = shift; +        my $value = $self->getRawValue; +        return GCPreProcess::reverseDate($value) if $self->{reverseDate}; +        return $value; +    } + +    sub setValue +    { +        my ($self, $text) = @_; +        $text = GCPreProcess::restoreDate($text) if $self->{reverseDate}; +        if ($text eq 'current') +        { +            $text = $self->getCurrentDate; +            $self->setChanged; +        } +        $text = GCUtils::timeToStr($text, $self->{format}) if $self->{format}; +        $self->{entry}->set_text($text); +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } + +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->{entry}->set_width_chars($value); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->{entry}->can_focus(!$locked); +        $self->{button}->set_sensitive(!$locked); +    } +} + +{ +    package GCFile; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCFile::ISA = ('Gtk2::HBox', 'GCGraphicComponent', 'GCPseudoHistoryComponent'); + +    use File::Basename; + +    sub selectValue +    { +	my $self = shift; + +        my $dialog = GCFileChooserDialog->new($self->{title}, +                                              $self->{mainParent}, +                                              $self->{type}, +                                              $self->{withFilter}); +		$dialog->set_filename($self->getValue); +        $dialog->set_pattern_filter($self->{patterns}) +            if $self->{patterns}; +		my $response = $dialog->run; +		if ($response eq 'ok') +		{ +	        $self->setValue($dialog->get_filename); +        } +        $dialog->hide; +        $self->{parent}->showMe; +    } +     +    sub setPatternFilter +    { +        my ($self, $patterns) = @_; +        $self->{patterns} = $patterns; +    } +     +    sub setType +    { +        my ($self, $type, $withFilter) = @_; +         +        if (($type ne $self->{type}) || ($withFilter != $self->{withFilter})) +        { +            $self->{dialog}->destroy +                if $self->{dialog}; +            $self->{dialog} = undef; +            $self->{type} = $type; +            $self->{withFilter} = $withFilter; +        } +    } + +    sub setTitle +    { +        my ($self, $title) = @_; +        $self->{title} = $title; +        if ($self->{dialog}) +        { +            $self->{dialog}->setTitle($title); +        } +    } + +    sub new +    { +        my ($proto, $parent, $title, $type, $withFilter, $defaultValue, $allowContextMenu, $fieldType) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +        bless ($self, $class); +        $self->{parent} = $parent; +        $self->getMainParent; + +        $title ||= $self->{parent}->{lang}->{PanelSelectFileTitle}; +        $self->{title} = $title; +        $type ||= 'open'; +        $self->{type} = $type; +        $withFilter = 0 if !$withFilter; +        $self->{withFilter} = $withFilter; + +        $self->{entry} = Gtk2::Entry->new; +         +        $self->{button} = new GCButton($self->{parent}->{lang}->{PanelSelectFileTitle}); + +        # For video/ebook/audio files, set file pattern filters +        if (($fieldType eq 'video') || ($fieldType eq 'ebook') || ($fieldType eq 'audio')) +        { +            $self->setPatternFilter([$self->{parent}->{lang}->{FileVideoFiles}, \@videoExtensions]) +                if ($fieldType eq 'video'); +            $self->setPatternFilter([$self->{parent}->{lang}->{FileEbookFiles}, \@ebookExtensions]) +                if ($fieldType eq 'ebook');      +            $self->setPatternFilter([$self->{parent}->{lang}->{FileAudioFiles}, \@audioExtensions]) +                if ($fieldType eq 'audio');        +            $self->{withFilter} = 1; +        } + +        $self->{button}->signal_connect('clicked' => sub { +            $self->selectValue; +        }); + +        if ($allowContextMenu) +        { +            my @subMenuFileChoose; +            $subMenuFileChoose[0] = Gtk2::ImageMenuItem->new_with_mnemonic($parent->{lang}->{ContextChooseFile}); +            $subMenuFileChoose[0]->signal_connect("activate" , sub {$self->selectValue}); +            $subMenuFileChoose[1] = Gtk2::ImageMenuItem->new_with_mnemonic($parent->{lang}->{ContextChooseFolder}); +            $subMenuFileChoose[1]->signal_connect("activate" , sub { +                $self->{type} = 'select-folder'; +                $self->{title} = $parent->{lang}->{ContextChooseFolder}; +                $self->selectValue; +            }); +            $self->{button}->setContextMenu(\@subMenuFileChoose); +            $self->{button}->enableContextMenu; +        } +         +        $self->pack_start($self->{entry}, 1, 1, 0); + +        if ($defaultValue) +        { +            $self->{defaultButton} = GCButton->newFromStock('gtk-undo', 0, $parent->{lang}->{PanelRestoreDefault}); +            $parent->{tooltips}->set_tip($self->{defaultButton}, +                $parent->{lang}->{PanelRestoreDefault}.$parent->{lang}->{Separator}.$defaultValue) +                    if $parent->{tooltips}; +            $self->pack_start($self->{defaultButton}, 0, 0, 0); +            $self->{defaultButton}->signal_connect('clicked' => sub { +                $self->setValue($defaultValue); +            }); +        } +         +        # Add the 'select file' button only if the field is not a url field +        $self->pack_start($self->{button}, 0, 0, 0) +            if ($fieldType ne 'url'); + +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->{entry}->signal_connect('changed' => sub {; +            $self->setChanged; +        }); +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub getValue +    { +        my $self = shift; +        return $self->{entry}->get_text; +    } + +    sub setValue +    { +        my ($self, $text) = @_; +        $self->{entry}->set_text($text); +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } + +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->{entry}->set_width_chars($value); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->{entry}->can_focus(!$locked); +        $self->{button}->set_sensitive(!$locked); +    } +} + +{ +    package GCUrl; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCUrl::ISA = ('GCFile'); + +    sub selectValue +    { +		my $self = shift; +		my $url = $self->getValue; +		return if !$url; +		$self->{opener}->launch($url, 'url'); +    } + +    sub new +    { +        my ($proto, $opener) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new(undef, '', 'url'); +        $self->{opener} = $opener; +        bless ($self, $class); +        return $self; +    } +} + +{ +    package GCButton; +     +    use Glib::Object::Subclass +                Gtk2::Button:: +    ; +     +    @GCButton::ISA = ('Gtk2::Button', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $label) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new($label); + +        bless ($self, $class); +        return $self; +    } +     +    sub newFromStock +    { +        my ($proto, $stock, $nolabel, $label) = @_; +        my $class = ref($proto) || $proto; + +        $nolabel = 0 if ($^O =~ /win32/i); + +        my $self = $class->SUPER::new_from_stock($stock); +         +        if ($nolabel) +        { +            my $tmpWidget = $self; +            $tmpWidget = $tmpWidget->child while ! $tmpWidget->isa('Gtk2::HBox'); +            ($tmpWidget->get_children)[1]->destroy; +        } +        elsif ($label) +        { +            my $tmpWidget = $self; +            $tmpWidget = $tmpWidget->child while ! $tmpWidget->isa('Gtk2::HBox'); +            ($tmpWidget->get_children)[1]->set_label($label);         +        } + +        bless ($self, $class); +        return $self; +    } +     +    sub isEmpty +    { +        my $self = shift; +         +        return 0; +    } +     +    sub getValue +    { +        my $self = shift; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; +        $self->setChanged; +        return $value; +    } + +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->set_sensitive(!$locked); +    } +     +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->set_size_request($value, -1); +    } +    sub setContextMenu +    { +        my ($self, $menu) = @_; +        $self->{popupContextMenu}=new Gtk2::Menu if !$self->{popupContextMenu}; +        for my $i(0..$#{$self->{popupContextMenu}->{items}}) +        { +            $self->{popupContextMenu}->remove($self->{popupContextMenu}->{items}->[$i]); +            $self->{popupContextMenu}->{items}->[$i]->destroy; +        } +        delete $self->{popupContextMenu}->{items}; +        for my $i(0..$#$menu) +        { +            $self->{popupContextMenu}->{items}->[$i]=$menu->[$i]; +            $self->{popupContextMenu}->append($self->{popupContextMenu}->{items}->[$i]); +        } +        $self->{popupContextMenu}->show_all; +    } +    sub enableContextMenu +    { +        my ($self,$button) = @_; +        #TODO enabled context for one button mouses +        $button=3 if !$button; +        $self->{contextMenuSignalHandler}=$self->signal_connect('button_press_event' => sub { +            my ($widget, $event) = @_; +            return 0 if $event->button ne $button; +            $self->{popupContextMenu}->popup(undef, undef, undef, undef, $event->button, $event->time); +            return 0; +        }) if !$self->{contextMenuSignalHandler}; +    } +    sub disableContextMenu +    { +        my $self = shift; +        if($self->{contextMenuSignalHandler}) +        { +            $self->signal_handler_disconnect($self->{contextMenuSignalHandler}); +            delete $self->{contextMenuSignalHandler}; +        } +    } +} + +{ +    package GCUrlButton; +     +    use Glib::Object::Subclass +                Gtk2::Button:: +    ; +     +    @GCUrlButton::ISA = ('GCButton', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $label, $opener) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new($label); +        $self->{opener}=$opener; +        $self->{defaultLabel} = $label; +        $self->{clicSignalHandler}=$self->signal_connect('clicked' => sub { +            $self->{opener}->launch($self->{url}, 'url'); +        }); +         + +        bless ($self, $class); +        return $self; +    } +         +    sub getValue +    { +        my $self = shift; + +        return $self->{value}; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; + +        $self->setChanged; +        my @urls=split ';',$value; +        if(scalar(@urls)>1) +        { +            my (@menu,$i,$url); +            foreach my $urlName(@urls) +            { +                $urlName =~ /^(.*?)##(.*)$/; +                my $name=$2; +                my $menuItem=Gtk2::MenuItem->new_with_label($name); +                $menuItem->signal_connect("activate" ,sub { +                    $self->{opener}->launch($_[1], 'url') +                },$1); +                push @menu,$menuItem; +            } +            $self->{url} =''; +            $self->setContextMenu(\@menu); +            $self->enableContextMenu(1); +            $self->lock(0); +            #$self->signal_handler_block($self->{clicSignalHandler}); +            $self->setLabel($self->{defaultLabel}); +        } +        else +        { +            if ($value =~ /^(.*?)##(.*)$/) +            { +                $self->{url} = $1; +                $self->setLabel($2); +            } +            else +            { +                $self->{url} = $value; +                $self->setLabel($self->{defaultLabel}); +            } +            $self->lock(!$self->{url}); +            $self->disableContextMenu; +            #$self->signal_handler_unblock($self->{clicHandler}); +        } +        $self->{value} = $value; +    } + +    sub setLabel +    { +        my ($self, $label) = @_; +        $self->set_label($label); +    } +} + +{ +    package GCCheckBox; +     +    use Glib::Object::Subclass +                Gtk2::CheckButton:: +    ; +     +    @GCCheckBox::ISA = ('Gtk2::CheckButton', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $label) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new($label); + +        bless ($self, $class); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; + +        $self->signal_connect('toggled' => sub { +            $self->setChanged; +        }); +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return 0; +    } +     +    sub getValue +    { +        my $self = shift; +        return 1 if ($self->get_active); +        return 0; +    } +     +    sub getValueAsText +    { +        my $self = shift; +        return 'true' if ($self->get_active); +        return 'false'; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; +        $self->set_active($value); +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(0); +    } + +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->set_sensitive(!$locked); +    } +} + +{ +    package GCCheckBoxWithIgnore; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCCheckBoxWithIgnore::ISA = ('Gtk2::HBox', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $parent) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new(0,0); +        bless ($self, $class); +         +        $self->{check}->[0] = new Gtk2::RadioButton(undef,$parent->{lang}->{CheckUndef}); +        $self->{group} = $self->{check}->[0]->get_group; +        $self->{check}->[1] = new Gtk2::RadioButton($self->{group},$parent->{lang}->{CheckNo}); +        $self->{check}->[2] = new Gtk2::RadioButton($self->{group},$parent->{lang}->{CheckYes}); +         +        $self->pack_start($self->{check}->[0], 0, 0, 10); +        $self->pack_start($self->{check}->[1], 0, 0, 10); +        $self->pack_start($self->{check}->[2], 0, 0, 10); +         +        return $self; +    } +     +    sub activateStateTracking +    { +        my $self = shift; + +        foreach (@{$self->{check}}) +        { +            $_->signal_connect('toggled' => sub { +                $self->setChanged; +            }); +        } +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->{check}->[0]->get_active; +    } +     +    sub getValue +    { +        my $self = shift; +        my $i = 0; +        foreach (@{$self->{check}}) +        { +            last if $self->{check}->[$i]->get_active; +            $i++; +        } +        $i--; +        return $i; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; +        $self->{check}->[$value + 1]->set_active(1); +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(-1); +    } + +    sub lock +    { +        my ($self, $locked) = @_; + +        $self->set_sensitive(!$locked); +    } +} + +{ +    package GCMultipleList; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCMultipleList::ISA = ('Gtk2::HBox', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $parent, $number, $labels, $withHistory, $readonly, $useFiles,$types) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new(0,0); + +        $self->{number} = $number; +        $self->{readonly} = $readonly; +        $self->{withHistory} = $withHistory; + +		my $hboxActions = new Gtk2::HBox(0,0); + +        my @histories; +        my @listColumns; +        my $i; +        for $i (0..($number - 1)) +        { +            push @histories, {'' => 1}; +            push @listColumns, ($labels->[$i] => 'text'); +            next if $readonly; +            if ($useFiles) +            { +                $self->{entries}->[$i] = GCFile->new($parent); +                $self->{entries}->[$i]->{entry}->signal_connect('activate' => sub { +                    $self->addValues; +                }); +            } +            else +            { +                if($types && $types->[$i] eq 'date') +                { +                    $self->{entries}->[$i] = GCDate->new($parent, $parent->{lang}, 1,$parent->{options}->dateFormat); +                    $self->{entries}->[$i]->{entry}->signal_connect('activate' => sub { +                        $self->addValues; +                        $self->{entries}->[0]->grab_focus; +                    }); +                    $self->{withHistoryField}->[$i]=0; +                } +                elsif ($withHistory && (!$types || $types->[$i] eq 'history')) +                { +                    $self->{entries}->[$i] = GCHistoryText->new; +                    $self->{entries}->[$i]->entry->signal_connect('activate' => sub { +                        my $widget = $self->{entries}->[$i]; +                        if ($widget->getValue) +                        { +                            $self->addValues; +                            # FIXME. It seems this does nothing: +                            $self->{entries}->[0]->grab_focus; +                        } +                        $widget->entry->signal_stop_emission_by_name('activate'); +                    }); +                    $self->{withHistoryField}->[$i]=1; +                } +                else +                { +                    $self->{entries}->[$i] = GCShortText->new; +                    $self->{entries}->[$i]->signal_connect('activate' => sub { +                        $self->addValues; +                        $self->{entries}->[0]->grab_focus; +                    }); +                    $self->{withHistoryField}->[$i]=0 if $withHistory; +                } +            } +            $self->{entries}->[$i]->setWidth(12); +            $hboxActions->pack_start($self->{entries}->[$i], 1, 1, 6); +        } + +        $self->{histories} = [\@histories]; + +        $self->{box} = new Gtk2::VBox(0,0); + +        # If list belongs to an expander, set box size to a reasonable size +        $self->{box}->{signalHandler} = $self->{box}->signal_connect('size-allocate' => sub { +            if (($self->{realParent}) && ($self->{realParent}->isa('GCExpander'))) +            { +                my $width = $self->allocation->width - ( 2 * $GCUtils::margin) ; +                $self->{box}->set_size_request(($width >= -1) ? $width : -1 , -1); +                return 0; +            } +        }); + +        $self->{list} = new Gtk2::SimpleList(@listColumns); +        for $i (0..($number - 1)) +        { +            $self->{list}->set_column_editable($i, 1); +        } +        $self->{list}->unset_rows_drag_source; +        $self->{list}->unset_rows_drag_dest; +        $self->{list}->set_reorderable(1); +        #($self->{list}->get_column(0)->get_cell_renderers)[0]->set('wrap-mode' => 'word'); + +        for $i (0..($number - 1)) +        { +            $self->{list}->get_column($i)->set_resizable(1); +        } +        my $scroll = new Gtk2::ScrolledWindow; +        $scroll->set_policy ('automatic', 'automatic'); +        $scroll->set_shadow_type('etched-in'); +        $scroll->set_size_request(-1, 120); +        $scroll->add($self->{list}); +        $self->{box}->pack_start($scroll, 1, 1, 2); +        if (!$readonly) +        { +            $self->{addButton} = GCButton->newFromStock('gtk-add', 0); +            $self->{addButton}->signal_connect('clicked' => sub { +                $self->addValues; +            }); +            $self->{removeButton} = GCButton->newFromStock('gtk-remove', 0); +            $hboxActions->pack_start($self->{addButton}, 0, 0, 6); +            $hboxActions->pack_start($self->{removeButton}, 0, 0, 6); +        } +        else +        { +            $self->{removeButton} = GCButton->newFromStock('gtk-remove', 0); +            $self->{clearButton} = GCButton->newFromStock('gtk-clear', 0); +            $self->{clearButton}->signal_connect('clicked' => sub { +                $self->clear; +            });             +            $hboxActions->pack_start($self->{removeButton}, 1, 0, 6); +            $hboxActions->pack_start($self->{clearButton}, 1, 0, 6); +        } +        $self->{box}->pack_start($hboxActions, 0, 0, 6) +            if $readonly < 2; + +        $self->{removeButton}->signal_connect('clicked' => sub { +            my @idx = $self->{list}->get_selected_indices; +            my $selected = $idx[0]; +            splice @{$self->{list}->{data}}, $selected, 1; +            $selected-- if ($selected >= scalar(@{$self->{list}->{data}})); +            $selected = 0 if $selected < 0 ; +            $self->{list}->select($selected); +        }); +         +        $self->{list}->signal_connect('key-press-event' => sub { +            my ($widget, $event) = @_; +            my $key = Gtk2::Gdk->keyval_name($event->keyval); +            if ((!$self->{readonly}) && ($key eq 'Delete')) +            { +                $self->{removeButton}->activate; +                return 1; +            } +            # Let key be managed by Gtk2 +            return 0; +        }); +         + +        $self->pack_start($self->{box},1,$self->{readonly},0); + +        bless ($self, $class); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->{list}->get_model->signal_connect('row-inserted' => sub { +            $self->setChanged; +        }); +        $self->{list}->get_model->signal_connect('row-deleted' => sub { +            $self->setChanged; +        }); +        $self->{list}->get_model->signal_connect('row-changed' => sub { +            $self->setChanged; +        }); +    } + +    sub expand +    { +        my $self = shift; +        $self->set_child_packing($self->{box},1,1,0,'start'); +    } + +    sub addValues +    { +        my ($self, @values) = @_; +        if (!$self->{readonly}) +        { +            for my $i (0..($self->{number} - 1)) +            { +                $values[$i] = $self->{entries}->[$i]->getValue if !$values[$i]; +                $self->{entries}->[$i]->addHistory($values[$i]) +                    if $self->{withHistory} && $self->{withHistoryField}->[$i]; +                $self->{entries}->[$i]->clear; +            } +        } +        # Check that at least one value is not empty +        my $isEmpty = 1; +        for my $val (@values) +        { +            if ($val) +            { +                $isEmpty = 0; +                last; +            } +        } +        if (!$isEmpty) +        { +            push @{$self->{list}->{data}}, \@values; +            $self->{list}->select($#{$self->{list}->{data}}); +            my $path = $self->{list}->get_selection->get_selected_rows; +            $self->{list}->scroll_to_cell($path) if $path; +        } +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub getValue +    { +        my $self = shift; +        my $formated = shift; + +        if ($formated) +        { +            return GCPreProcess::multipleList($self->{list}->{data}, $self->{number}); +        } +        else +        { +            # As data in list is a tied array, we need to copy all the values +            my @value; +            foreach (@{$self->{list}->{data}}) +            { +                push @value, []; +                foreach my $col(@$_) +                { +                    push @{$value[-1]}, $col; +                } +            } +            return \@value; +        } +    } +     +    sub setValue +    { +        my ($self, $value) = @_; + +        if (ref($value) eq 'ARRAY') +        { +            @{$self->{list}->{data}} = @{$value}; +        } +        else +        { +            # The separator used was ; instead of , +            $value =~ s/;/,/g if $value !~ /,/; +            @{$self->{list}->{data}} = (); +            my @values = split m/,/, $value; +            foreach my $entry (@values) +            { +                my @items = split m/;/, $entry; +                s/^\s*// foreach(@items); +                push @{$self->{list}->{data}}, \@items; +            } +        } +    } +     +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +        return if $self->{readonly}; +        $self->{addButton}->set_sensitive(!$locked); +        $self->{removeButton}->set_sensitive(!$locked); +        foreach (@{$self->{entries}}) +        { +            $_->lock($locked); +        } +    } + +    sub addHistory +    { +        my $self = shift; +        my $value = (scalar @_) ? shift : $self->getValue; +        my $noUpdate = shift; + +        return if $self->{readonly}; +        my $i; +        my $item; +        if (ref($value) eq 'ARRAY') +        { +            foreach (@$value) +            { +                $i = 0; +                foreach $item(@$_) +                { +                    $self->{entries}->[$i]->addHistory($item, $noUpdate) if $self->{withHistoryField}->[$i]; +                    $i++; +                } +            } +        } +        else +        { +            # The separator used was ; instead of , +            $value =~ s/;/,/g if $value !~ /,/; +            my @values = split m/,/, $value; +            foreach (@values) +            { +                my @items = split m/;/; +                $i = 0; +                foreach my $item(@items) +                { +                    $self->{entries}->[$i]->addHistory($item, $noUpdate) if $self->{withHistoryField}->[$i]; +                    $i++; +                } +                #push @{$self->{list}->{data}}, \@items; +            } +        } +    } + +    sub setDropDown +    { +        my $self = shift; +         +        my $i = 0; +        foreach (@{$self->{entries}}) +        { +            $_->setDropDown if $self->{withHistoryField}->[$i]; +            $i++; +        } +    } + +    sub getValues +    { +        my $self = shift; +        return [] if $self->{readonly}; +        my @array; +        my $i=0; +        foreach (@{$self->{entries}}) +        { +            my $val=[]; +            $val=$_->getValues if ($self->{withHistoryField}->[$i++]); +            push @array, $val; +        } +        # = sort keys %{$self->{history}}; +        return \@array; +    } +     +    sub setValues +    { +        my ($self, $values) = @_; +        return if $self->{readonly}; +        my $i = 0; +        foreach (@$values) +        { +            $self->{entries}->[$i]->setValues($_) if ($self->{withHistoryField}->[$i]); +            $i++; +        } +    } +} + + +{ +    package GCItemImage; +     +    use Glib::Object::Subclass +                Gtk2::Image:: +    ; +     +    @GCItemImage::ISA = ('Gtk2::Image', 'GCGraphicComponent'); + +    use File::Spec; +    use File::Basename; +     +    sub new +    { +        my ($proto, $options, $parent, $fixedSize, $width, $height) = @_; +        my $class = ref($proto) || $proto; +#        my $self  = Gtk2::Image->new_from_file($parent->{defaultImage}); +        my $self  = Gtk2::Image->new; +        $self->{options} = $options; +        #$self->{defaultImage} = $defaultImage; +        $self->{parent} = $parent; +        $self->{displayedImage} = ''; +        $self->{fixedSize} = $fixedSize; +        bless ($self, $class); +        if ($width && $height) +        { +            $self->{width} = $width; +            $self->{height} = $height; +        } +        else +        { +            $self->{width} = 120; +            $self->{height} = 160; +        } +        $self->{immediate} = 0; +        return $self; +    } + +    sub setImmediate +    { +        my ($self) = @_; +        $self->{immediate} = 1; +    } +    sub activateStateTracking +    { +        my $self = shift; +        $self->{trackState} = 1; +    } + +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub setValue +    { +        my ($self, $displayedImage, $placer) = @_; +        $self->{displayedImage} = $displayedImage; +        $self->setChanged if $self->{trackState}; +        if ($self->{immediate}) +        { +            $self->setPicture; +        } +        else +        { +            Glib::Source->remove($self->{timer}) +                if $self->{timer}; +            $self->{timer} = Glib::Timeout->add(100, sub { +                $self->setPicture; +                $placer->placeImg if $placer; +                return 0; +            }); +        } +    } + +    sub setPicture +    { +        my $self = shift; +        $self->{timer} = 0; +        my $displayedImage = GCUtils::getDisplayedImage($self->{displayedImage}, +                                                        $self->{parent}->{defaultImage}, +                                                        $self->{options}->file); +        my $pixbuf; +        eval +        { +            $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($displayedImage); +        }; +        if ($@) +        { +            $displayedImage = $self->{parent}->{defaultImage}; +            $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($displayedImage); +        } +        $self->{realImage} = $displayedImage; +        $pixbuf = GCUtils::scaleMaxPixbuf($pixbuf, $self->{width}, $self->{height}); +        $self->set_from_pixbuf($pixbuf); +        $self->set_size_request($self->{width}, $self->{height}) if $self->{fixedSize}; +    } +     +    sub getValue +    { +        my $self = shift; +        return $self->{displayedImage}; +    } + +    sub getFile +    { +        my $self = shift; +        return $self->{realImage}; +    } + +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } +     +    sub lock +    { +        my ($self, $locked) = @_; +    } +     +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->{width} = $value; +    } + +    sub setHeight +    { +        my ($self, $value) = @_; +        $self->{height} = $value; +    } +     +    sub getSize +    { +        my $self = shift; +        my $pixbuf = $self->get_pixbuf; +        return ($pixbuf->get_width, $pixbuf->get_height); +    } +} + + +our $hasGnome2VFS; +BEGIN { +    eval 'use Gnome2::VFS'; +    if ($@) +    { +        $hasGnome2VFS = 0; +    } +    else +    { +        $hasGnome2VFS = 1; +        Gnome2::VFS->init(); +    } +} + +{ +    package GCImageButton; + +    use Glib::Object::Subclass +                Gtk2::Button:: +    ; +     +    @GCImageButton::ISA = ('Gtk2::Button', 'GCGraphicComponent'); + +    use File::Basename; +    use Encode; + +    sub animateImg +    { +        my ($self, $from, $to) = @_; +        my $pixbuf1 = Gtk2::Gdk::Pixbuf->new_from_file($from); +        $pixbuf1 = GCUtils::scaleMaxPixbuf($pixbuf1, $self->{img}->{width}, $self->{img}->{height}); +        my $pixbuf2 = Gtk2::Gdk::Pixbuf->new_from_file($to); +        $pixbuf2 = GCUtils::scaleMaxPixbuf($pixbuf2, $self->{img}->{width}, $self->{img}->{height}); +        my $height = $pixbuf2->get_height; +        my $width = $pixbuf2->get_width; +        foreach my $i(0..20) +        { +            Glib::Timeout->add(30*$i, sub { +                my $pixbufA = $pixbuf1->copy; +                my $pixbufB = $pixbuf2->copy; +                $pixbufA->composite($pixbufB, 0, 0, int($width - (($i/20)*$width)), $height, 0, 0, 1, 1, 'nearest', 255); +                $self->{img}->set_from_pixbuf($pixbufB); +            }); +        } +    } + +    sub setImg +    { +        my ($self, $value) = @_; +        $self->{img}->setValue($value, $self); +    } + +    sub placeImg +    { +        my ($self) = @_; +        my ($picWidth, $picHeight) = $self->{img}->getSize; +        my ($buttonWidth, $buttonHeight) = ($self->allocation->width, $self->allocation->height); +        my $x = ($buttonWidth - $picWidth - $GCUtils::margin)/ 2; +        my $y = ($buttonHeight -$picHeight - $GCUtils::margin)/ 2; + +        # Don't allow negative positions, can happen when button has not been allocated a width/height yet +        $x = 0 if ($x < 0); +        $y = 0 if ($y < 0); +         +        $self->{layout}->move($self->{img}, $x, $y); +    } + +    sub changeState +    { +        my $self = shift; +        if ($self->{trackState}) +        { +            if ($self->{flipped}) +            { +                $self->{linkedComponent}->setChanged; +            } +            else +            { +                $self->setChanged; +            } +        } +    } + +    sub clearImage +    { +        my $self = shift; + +        $self->changeState; +        $self->{mainParent}->checkPictureToBeRemoved($self->{imageFile}); +        $self->setValueWithParent(''); +    } + +    sub changeImage +    { +        my ($self, $fileName) = @_; +        return 0 if $self->{locked}; +        if (!$fileName) +        { +            my $imageDialog = GCFileChooserDialog->new($self->{parent}->{lang}->{PanelImageTitle}, $self->{mainParent}, 'open'); +            $imageDialog->setWithImagePreview(1); + +            my $currentFile = $self->{img}->getValue; +            if ($currentFile) +            { +                $imageDialog->set_filename($currentFile); +            } +            else +            { +                $imageDialog->set_filename($self->{previousDirectory}); +            } +            my $response = $imageDialog->run; +            $fileName = $imageDialog->get_filename; +            $imageDialog->destroy; + +            $self->{parent}->showMe; +            if ($response eq 'ok') +            { +                $self->{previousDirectory} = dirname($fileName); +                $self->setChanged if $self->{trackState}; +            } +            else +            { +                return; +            } +        } + +        my $ref = ($self->{flipped} ? $self->{backPic} : $self->{imageFile}); +        if ($fileName ne $ref) +        { +            $self->{mainParent}->checkPictureToBeRemoved($ref); +            $self->changeState; +        } +        my $image = $self->{mainParent}->transformPicturePath($fileName); +        $self->setValueWithParent($image); +        return;         +    } +     +    sub isEmpty +    { +        my $self = shift; +         +        return $self->getValue eq ''; +    } +     +    sub setValue +    { +        my ($self, $value) = @_;         +        $self->setChanged if $self->{trackState}; +        $self->setActualValue($value); +    } + +    sub setValueWithParent +    { +        my ($self, $value, $keepWatcher) = @_; +         +        $self->setActualValue($value, $keepWatcher, $self->{flipped}); +        if ($self->{isCover} && !$self->{flipped}) +        { +            $self->{mainParent}->{items}->updateSelectedItemInfoFromPanel(0, [$self->{name}]); +            $self->{hasChanged} = 0 if $self->{parent} eq $self->{mainParent}->{panel} && !$keepWatcher; +        } +    } + +    sub setActualValue +    { +        my ($self, $value, $keepWatcher, $flipped) = @_; +        Glib::Source->remove($self->{fileWatcher}) +            if $self->{fileWatcher} && !$keepWatcher; +        if ($flipped) +        { +            $self->{backPic} = $value; +        } +        else +        { +            $self->{imageFile} = $value; +        } +        $self->setImg($value); +    } + +    sub getValue +    { +        my $self = shift; +        if ($self->{flipped}) +        { +            return $self->{imageFile}; +        } +        return $self->{img}->getValue; +    } + +    sub setLinkedActivated +    { +        my ($self, $value) = @_; +        $self->flipImage(1) if $self->{flipped}; +        $self->{flipActivated} = $value; +    } + +    sub flipImage +    { +        my ($self, $noButton) = @_; +        my $newLabel; +        if ($self->{flipped}) +        { +            $self->setImg($self->{imageFile}); +            $self->{frontFlipImage}->show if !$noButton; +            $self->{backFlipImage}->hide; +            #$self->animateImg($self->{backPic}, $self->{imageFile}); +        } +        else +        { +            $self->setImg($self->{backPic}); +            $self->{backFlipImage}->show if !$noButton; +            $self->{frontFlipImage}->hide; +            #$self->animateImg($self->{imageFile}, $self->{backPic}); +        } +        $self->{flipped} = !$self->{flipped}; +    } + +    sub setLinkedValue +    { +        my ($self, $linkedValue) = @_; +        $self->setChanged if $self->{trackState}; +        $self->{backPic} = $linkedValue; +        $self->setImg($linkedValue) if $self->{flipped}; +    } + +    sub getLinkedValue +    { +        my ($self, $linkedValue) = @_; +        return $self->{backPic}; +    } + +    sub setLinkedComponent +    { +        my ($self, $linked) = @_; +        $self->{linkedComponent} = $linked; + +        $self->{flipActivated} = 1; +        $self->{frontFlipImage} = Gtk2::Image->new_from_file($ENV{GCS_SHARE_DIR}.'/overlays/flip.png'); +        $self->{frontFlipImage}->set_no_show_all(1); +        $self->{backFlipImage} = Gtk2::Image->new_from_file($ENV{GCS_SHARE_DIR}.'/overlays/flip2.png'); +        $self->{backFlipImage}->set_no_show_all(1); +        my $pixbuf = $self->{frontFlipImage}->get_pixbuf; +        my ($picWidth, $picHeight) = ($pixbuf->get_width, $pixbuf->get_height); + +        $self->{addedFlipButton} = 0; +        $self->signal_connect('enter' => sub { +            return if ! $self->{flipActivated}; +            if (!$self->{addedFlipButton}) +            { +                $self->{flipX} = $self->{width} - $picWidth - $GCUtils::margin; +                $self->{flipY} = $self->{height} - $picHeight - $GCUtils::margin; +                $self->{layout}->put($self->{frontFlipImage}, +                                     $self->{flipX}, +                                     $self->{flipY}); +                $self->{layout}->put($self->{backFlipImage}, +                                     $self->{flipX}, +                                     $self->{flipY}); +                $self->{addedFlipButton} = 1; +            } +            if ($self->{flipped}) +            { +                $self->{backFlipImage}->show; +            } +            else +            { +                $self->{frontFlipImage}->show; +            } +        }); +        $self->signal_connect('leave' => sub { +            $self->{frontFlipImage}->hide; +            $self->{backFlipImage}->hide; +        }); +        $self->signal_connect('button-release-event' => sub { +            return 0 if ! $self->{flipActivated}; +            my ($button, $event) = @_; +            my ($x, $y) = $event->get_coords; +            if (($x > $self->{flipX}) && ($y > $self->{flipY})) +            { +                $self->flipImage; +                $self->set_sensitive(0); +                $self->released; +                $self->set_sensitive(1); +                return 1; +            } +            else +            { +                return 0; +            } +        }); +         +        $self->signal_connect('key-press-event' => sub { +            return 0 if ! $self->{flipActivated}; +            my ($widget, $event) = @_; +            my $key = Gtk2::Gdk->keyval_name($event->keyval); +             +            if (($key eq 'f') || ($key eq 'BackSpace')) +            { +                $self->flipImage; +                return 1; +            } +            return 0; +        }); +         +        $self->signal_connect('query_tooltip' => sub { +            my ($window, $x, $y, $keyboard_mode, $tip) = @_; +            return if $self->{settingTip}; +            $self->{settingTip} = 1; +            if ($self->{flipActivated} && ($x > $self->{flipX}) && ($y > $self->{flipY})) +            { +                $self->{tooltips}->set_tip($self, $self->{flipped} ? +                                                  $self->{parent}->{lang}->{ContextImgFront} : +                                                  $self->{parent}->{lang}->{ContextImgBack}); +            } +            else +            { +                $self->{tooltips}->set_tip($self, $self->{tip}); +            } +            $self->{settingTip} = 0; +            return 0; +        }); +         +    } + +    sub clear +    { +        my $self = shift; +        $self->setValue(''); +    } + +    sub new +    { +        my ($proto, $parent, $img, $isCover, $default) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; +        bless ($self, $class); + +        $default = 'view' if !$default; + +        $self->{layout} = new Gtk2::Fixed; +        $self->{layout}->put($img, 0, 0); +        $self->add($self->{layout}); +         +        $self->{img} = $img; +        $self->{default} = $default; +        #$self->set_size_request(130,170); +        $self->{width} = -1; +        $self->{height} = -1; +        $self->{imageFile} = $img->getValue; + +        # True if this is the cover used in image mode +        $self->{isCover} = $isCover; + +        $self->{parent} = $parent; +        $self->getMainParent; +        $self->{tooltips} = $self->{mainParent}->{tooltips}; + +        $self->{tip} = ($default eq 'open') ? $parent->{lang}->{PanelImageTipOpen} : $parent->{lang}->{PanelImageTipView}; +        $self->{tip} .= $parent->{lang}->{PanelImageTipMenu}; +        $self->{tooltips}->set_tip($self, $self->{tip}); + +        $self->signal_connect('button_press_event' => sub { +            my ($widget, $event) = @_; +            return 0 if $event->button ne 3; +            $self->createContextMenu(); +            $self->{imgContext}->popup(undef, undef, undef, undef, $event->button, $event->time); +            return 0; +        }); + +        $self->signal_connect('clicked' => sub { +            $self->changeImage if $self->{default} eq 'open'; +            $self->showImage if $self->{default} eq 'view'; +            return 1; +        }); + +        #Drag and drop a picture on a button +        $self->drag_dest_set('all', ['copy','private','default','move','link','ask']); +        my $target_list = Gtk2::TargetList->new(); +        my $atom1 = Gtk2::Gdk::Atom->new('text/uri-list'); +        my $atom2 = Gtk2::Gdk::Atom->new('text/plain'); +        $target_list->add($atom1, 0, 0); +        $target_list->add($atom2, 0, 0); +        if ($^O =~ /win32/i) +        { +            my $atom2 = Gtk2::Gdk::Atom->new('DROPFILES_DND'); +            $target_list->add($atom2, 0, 0); +        } +        $self->drag_dest_set_target_list($target_list); +        $self->signal_connect(drag_data_received => sub { +            my ($widget, $context, $widget_x, $widget_y, $data, $info,$time) = @_; +            my @files = split /\n/, $data->data; +            my $fileName = $files[0]; +            if ($fileName =~ /^http/) +            { +                $fileName = $self->{mainParent}->downloadPicture($fileName); +            } +            else +            { +                $fileName =  Glib::filename_from_uri  $fileName;  +                $fileName = decode('utf8', $fileName);            +                $fileName =~ s|^file://?(.*)\W*$|$1|; +                $fileName =~ s|^/*|| if ($^O =~ /win32/i); +                $fileName =~ s/.$//ms; +                $fileName =~ s/%20/ /g; +            } +            $self->changeImage($fileName); +        }); + +        $self->{previousDirectory} = ''; +        $self->{flipped} = 0; +        $self->{flipActivated} = 0; + +        return $self; +    } +     +    sub createContextMenu +    { +        my $self = shift; +         +        my $parent; +        $parent = $self->{parent}; + +        $self->{imgContext} = new Gtk2::Menu; + +        if ($parent->{options}->tearoffMenus) +        { +            $self->{imgContext}->append(Gtk2::TearoffMenuItem->new()); +        } + +        $self->{itemOpen} = Gtk2::ImageMenuItem->new_with_mnemonic($parent->{lang}->{ContextChooseImage}); +        my $itemOpenImage = Gtk2::Image->new_from_stock('gtk-open', 'menu'); +        $self->{itemOpen}->set_image($itemOpenImage); +        # This item will be deactivated if the component is locked +        $self->{itemOpen}->set_sensitive(!$self->{locked}); +        $self->{itemOpen}->signal_connect("activate" , sub { +            $self->changeImage; +        }); +        $self->{imgContext}->append($self->{itemOpen}); +        $self->{itemShow} = Gtk2::ImageMenuItem->new_from_stock('gtk-zoom-100',undef); +        $self->{itemShow}->signal_connect("activate" , sub { +            $self->showImage; +        }); +        # Disable for default image +        $self->{itemShow}->set_sensitive(0) if $self->isDefaultImage(); +        $self->{imgContext}->append($self->{itemShow}); + +        if ($self->{linkedComponent}) +        { +            $self->{itemFlip} = Gtk2::MenuItem->new($self->{flipped} ? +                                                        $parent->{lang}->{ContextImgFront} : +                                                        $parent->{lang}->{ContextImgBack}); +            $self->{itemFlip}->signal_connect("activate" , sub { +                $self->flipImage; +            }); +            $self->{imgContext}->append($self->{itemFlip}); +        } + +        $self->{itemClear} = Gtk2::ImageMenuItem->new_from_stock('gtk-clear',undef); +        # This item will be deactivated if the component is locked +        $self->{itemClear}->set_sensitive(!$self->{locked}); +        $self->{itemClear}->signal_connect("activate" , sub { +            $self->clearImage; +        }); +        $self->{imgContext}->append($self->{itemClear});         +        # Disable for default image +        $self->{itemClear}->set_sensitive(0) if $self->isDefaultImage(); +        $self->{imgContext}->show_all; + +        my $itemOpenWith = Gtk2::MenuItem->new_with_mnemonic($parent->{lang}->{ContextOpenWith}); +        $self->{menuOpenWith} = Gtk2::Menu->new; + +        if ($hasGnome2VFS && ($parent->{options}->programs eq 'system' || $parent->{options}->imageEditor eq '')) +        {  +            # Get applications for mime types corresponding with image + +            # Get all editors/viewers for jpeg file format +            my $mimeTest = Gnome2::VFS::Mime::Type->new ("image/jpeg"); +            my @mimeList = $mimeTest->get_short_list_applications; + +            # Add applications to open with list +            foreach (@mimeList) +            { +                my $launchApp = $_; +                my $item = Gtk2::MenuItem->new_with_mnemonic($launchApp->get_name); +                $item->signal_connect ('activate' => sub { +                        $self->openWith($launchApp); +                }); +                $self->{menuOpenWith}->append($item); +            } + +            #Gnome2::VFS -> shutdown(); +        } +        elsif ($parent->{options}->programs eq 'system' || $parent->{options}->imageEditor eq '') +        { +            # Can't parse applications, so use system default app   +            my $item = Gtk2::MenuItem->new_with_mnemonic($parent->{lang}->{ContextImageEditor}); + +            my $command; +            $command = ($^O =~ /win32/i) ? '' +                     : ($^O =~ /macos/i) ? '/usr/bin/open' +                     :                     $ENV{GCS_SHARE_DIR}.'/helpers/xdg-open'; + +            # Not sure if this is correct, haven't tested with Windows: +            if ($^O =~ /win32/i) +            { +                $command = '"'.$command.'"' if $command; +            } + +            $item->signal_connect ('activate' => sub { +                     $self->openWithImageEditor($command); +            }); + +            $self->{menuOpenWith}->append($item); +        } +        else +        { +            # Use user defined app   +            my $item = Gtk2::MenuItem->new_with_mnemonic($parent->{lang}->{ContextImageEditor}); +            $item->signal_connect ('activate' => sub { +                     $self->openWithImageEditor($parent->{options}->imageEditor); +            }); + +            $self->{menuOpenWith}->append($item); +        } + + +        $itemOpenWith->set_submenu($self->{menuOpenWith}); +        +        # Disable for default image +        $itemOpenWith->set_sensitive(0) if $self->isDefaultImage(); + +        $self->{imgContext}->append($itemOpenWith); +        $self->{imgContext}->show_all; + +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->{trackState} = 1; +    } + +    sub lock +    { +        my ($self, $locked) = @_; +     +        $self->{locked} = $locked; +    } +     +    sub showImage +    { +        my $self = shift; +        $self->{mainParent}->launch($self->{img}->getValue, 'image'); +    } +     +    sub isDefaultImage +    { +        my ($self) = @_; + +        if ($self->{img}->getFile eq $self->{parent}->{defaultImage}) +        { +            return 1; +        } +        else +        { +            return 0; +        } +    } + +    sub openWith +    { +        my ($self, $app) = @_; +        my $cmd; +        my $escFileName; + +        # Ultra hacky workaround, because $app->{launch} segfaults. See http://bugzilla.gnome.org/show_bug.cgi?id=315049 +        # Probably should change to gvfs when perl modules are available + +        if ($app->{command} =~ m/(\w*)/) +        { +            $cmd = $1; +        } + +        $escFileName = $self->{img}->getFile; +        $escFileName =~ s/\ /%20/g; +        $self->editPicture("$cmd file://$escFileName"); +    } + +    sub openWithImageEditor +    { +        my ($self, $editor) = @_; +        my $file = $self->{img}->getFile; +        $file =~ s|/|\\|g if ($^O =~ /win32/i); +        $self->editPicture("$editor \"$file\""); +    } + +    sub editPicture +    { +        my ($self, $commandLine) = @_; +        my $file = $self->{img}->getFile; + +        my $flipped = $self->{flipped}; +        $self->{fileWatchDays} = -M $file;  +        $self->{fileWatcher} = Glib::Timeout->add(1000, sub { +            my $currentDays = -M $file; +            if ($currentDays < $self->{fileWatchDays}) +            { +                $self->changeState; +                # We remove it from the pixbuf cache in items view. Useful +                # for detailed list to be sure it will be re-loaded +                delete $self->{mainParent}->{itemsView}->{cache}->{$file} +                    if $self->{mainParent}->{itemsView}->{cache}; +                $self->setValueWithParent($self->{img}->getValue, 1, $flipped); +                $self->{fileWatchDays} = $currentDays;  +            } +            return 1; +        }); +        $self->{mainParent}->launch($commandLine, 'program', 1); +    } + +    sub setWidth +    { +        my ($self, $value) = @_; +        $self->{width} = $value; +        $self->set_size_request($value, $self->{height}); +        $self->{img}->setWidth($value - $GCUtils::margin); +    } + +    sub setHeight +    { +        my ($self, $value) = @_; +        $self->{height} = $value; +        $self->set_size_request($self->{width}, $value); +        $self->{img}->setHeight($value - $GCUtils::margin); +    } +     +} + +{ +    package GCMenuList; + +    use Glib::Object::Subclass +                Gtk2::ComboBox:: +    ; +     +    @GCMenuList::ISA = ('Gtk2::ComboBox', 'GCGraphicComponent'); + +    our $separatorValue = 'GCSSeparator'; + +    sub isEmpty +    { +        my $self = shift; +         +        return 1 if ! defined $self->get_active_iter; +        return ($self->{listModel}->get($self->get_active_iter))[1] eq ''; +        my $idx = $self->get_history; +        $idx-- if $idx >= $self->{separatorPosition}; +        $idx = 0 if $idx < 0; +        return $self->{'values'}->[$idx]->{displayed} eq ''; +    } +     +    sub valueToDisplayed +    { +        my ($self, $value) = @_; +     +        foreach (@{$self->{'values'}}) +        { +            return $_->{displayed} if $_->{value} eq $value +        } +        return ''; +    } +     +    sub getValue +    { +        my ($self, $formatted) = @_; +        my $iter = $self->get_active_iter; +        my $value = ''; +        $value = ($self->{listModel}->get($iter))[0] if $iter; +        $value = $self->valueToDisplayed($value) if $formatted; +        return $value; +    } + +    sub getDisplayedValue +    { +        my $self = shift; +        my $iter = $self->get_active_iter; +        return ($self->{listModel}->get($iter))[1] if $iter; +        return ''; +    } + +    sub setValue +    { +        my ($self, $value) = @_; + +        $value = 0 if !$value; +        my $i = 0; +        if ($value) +        { +    		foreach (@{$self->{values}}) +            { +                last if $_->{value} eq $value; +                $i++; +            } +        } +        $i++ if $i >= $self->{separatorPosition}; +        $i-- if ($self->{default} == -1) && ($i >= $self->{count}); +        $self->set_active($i) +            if ($i < scalar(@{$self->{values}})); +    } + +    sub clear +    { +        my $self = shift; +        $self->set_active(0); +    } + +    sub lock +    { +        my ($self, $locked) = @_; +         +        $self->set_sensitive(!$locked); +    } +     +    sub getValues +    { +        my $self = shift; +         +        my @values; +        return $self->{values}; +    } +     +    sub setValues +    { +        my ($self, $values, $separatorPosition, $preserveValue) = @_; +        if ($self->{title}) +        { +            $separatorPosition = 1; +            unshift @$values, {value => -1, displayed => $self->{title}}; +        } +        my $model = $self->{listModel}; +        my $previous = $self->getValue if $preserveValue; +        $self->{values} = $values; +        $self->{separatorPosition} = $separatorPosition || 9999; + +        $model->clear; +        my $i = 0; +        foreach (@$values) +        { +            if ($i == $self->{separatorPosition}) +            { +                $model->set($model->append, 0 => $GCMenuList::separatorValue, 1 => ''); +                $i++; +            } +            $model->set($model->append, +                        0 => $_->{value}, +                        1 => $_->{displayed}); +            $i++; +        } + +        $self->{count} = $i; +        $self->setValue($previous) if $preserveValue; +        $self->set_active(0) if !$preserveValue; +    } +     +    sub setLastForDefault +    { +        my $self = shift; +        $self->{default} = -1; +    } + +    sub setTitle +    { +        my ($self, $title) = @_; +        $self->{title} = $title; +    } + +    sub new +    { +        my ($proto, $values, $separatorPosition) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; +        bless ($self, $class); + +        $self->{listModel} = Gtk2::ListStore->new('Glib::String', 'Glib::String'); +        $self->set_model($self->{listModel}); +        my $renderer = Gtk2::CellRendererText->new; +        $self->pack_start($renderer, 1); +        $self->add_attribute($renderer, text => 1); +        $self->set_row_separator_func(sub { +            my ($model, $iter) = @_; +            my @values = $model->get($iter, 0); +            my $val = ''; +            $val = $values[0] if defined $values[0]; +            return $val eq $GCMenuList::separatorValue; +        }); + +        $self->setValues($values, $separatorPosition) if $values; +        $self->{default} = 0; +        $self->set_focus_on_click(1); +        return $self; +    } + +    sub activateStateTracking +    { +        my $self = shift; +        $self->signal_connect('changed' => sub { +            $self->setChanged; +        }); +    } +} + +{ +    package GCHeaderLabel; + +    use Glib::Object::Subclass +                Gtk2::Label:: +    ; +     +    @GCHeaderLabel::ISA = ('Gtk2::Label', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $label) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; +        bless ($self, $class); + +        $self->setText($label); +        $self->set_alignment(0,1); +         +        return $self; +    } +     +    sub setText +    { +        my ($self, $label) = @_; +        $self->set_markup('<b>'.$label.'</b>');         +    } +} + +{ +    package GCLabel; +     +    use Glib::Object::Subclass +                Gtk2::Label:: +    ; +     +    @GCLabel::ISA = ('Gtk2::Label', 'GCGraphicComponent'); + +    sub new +    { +        my ($proto, $label, $disableMarkup) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; +        bless ($self, $class); +        $self->set_markup($label) unless $disableMarkup; +        $self->set_label($label) if $disableMarkup; +        $self->set_alignment(0,0.5); +        return $self; +    } +} + +{ +    package GCColorLabel; +     +    use Glib::Object::Subclass +                Gtk2::EventBox:: +    ; +     +    @GCColorLabel::ISA = ('Gtk2::EventBox', 'GCGraphicComponent', 'GCPseudoHistoryComponent'); + +    sub new +    { +        my ($proto, $color, $listType) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; + +        bless ($self, $class); + +        $listType = 0 if !$listType; +        $self->modify_bg('normal', $color); +        $self->{label} = Gtk2::Label->new; +        $self->{label}->show; +        $self->{hboxFill} = new Gtk2::HBox(0,0); +        $self->{hboxFill}->pack_start($self->{label},1,1,0); +        $self->add($self->{hboxFill}); +        $self->set_alignment(0,0.5); +        $self->initHistory($listType); + +        return $self; +    } + +    sub acceptMarkup +    { +        my $self = shift; +        return 1; +    } + +    sub setMarkup +    { +        my ($self, $text) = @_; +         +        $self->{label}->set_markup($text); +    } + +    sub getValue +    { +        my $self = shift; + +        (my $label = $self->{label}->get_label) =~ s/<.*?>(.*?)<\/.*?>/$1/g; +        return $label; +    } + +    sub setBgColor +    { +        my ($self, $color) = @_; +        $self->modify_bg('normal', $color); +    } + +    sub set_justify +    { +        my ($self, $value) = @_; +        $self->{label}->set_justify($value); +        $self->{label}->set_alignment(0.5,0) if $value eq 'center'; +        $self->{label}->set_alignment(1,0) if $value eq 'right'; +    } +     +    sub AUTOLOAD +    { +        my $self = shift; +        my $name = our $AUTOLOAD; +        return if $name =~ /::DESTROY$/; +        $name =~ s/.*?::(.*)/$1/; +        $self->{label}->$name(@_); +    } +} + +{ +    package GCColorLink; +     +    use Glib::Object::Subclass +                Gtk2::EventBox:: +    ; +     +    @GCColorLink::ISA = ('GCColorLabel'); + +    sub new +    { +        my ($proto, $color, $opener) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new($color); +        bless ($self, $class); +        $self->{opener} = $opener; +        $self->signal_connect('button-release-event' => sub { +            my $value = $self->getValue; +            return if !$value; +            $self->{opener}->launch($value, 'url'); +        }); +        $self->signal_connect('enter-notify-event' => sub { +            $self->window->set_cursor(Gtk2::Gdk::Cursor->new('hand2')) +                if $self->getValue; +        }); +        #$self->window->set_cursor(Gtk2::Gdk::Cursor->new('watch')); +        return $self; +    } +} + +{ +    package GCColorLongLabel; + +    use Glib::Object::Subclass +                Gtk2::TextView:: +    ; +     +    @GCColorLongLabel::ISA = ('Gtk2::TextView', 'GCGraphicComponent'); + +    sub new +    { +        my ($proto, $color) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +        bless ($self, $class); + +        $self->set_editable(0); +        $self->set_wrap_mode('word'); +        $self->{background} = $color; +        $self->modify_base('normal', $color); +        $self->modify_bg('normal', $color); +        $self->set_border_width($GCUtils::halfMargin); + +        my $layout = $self->create_pango_layout('G'); +        my (undef, $rect) = $layout->get_pixel_extents; +        $self->{em} = 1.5 * $rect->{height};         + + +        return $self; +    } +     +    sub acceptMarkup +    { +        my $self = shift; +        return 1; +    } + +    sub setMarkup +    { +        my ($self, $text) = @_; +         +        #$text =~ s/&/&/g; +        if ($self->{resize}) +        { +            $self->resize; +        } +        else +        { +            my $buffer = $self->get_buffer; +            $self->get_buffer->set_text(''); +            $text =~ s|<span ([^>]*?)>([^<]*?)</span>|$2|; +            if ($self->{spanTag}) +            { +                $buffer->get('tag-table')->remove($self->{spanTag}); +            } +            $self->{spanTag} = $buffer->create_tag('span', $self->getTagFromSpan($1)); +            $buffer->insert_with_tags_by_name ($buffer->get_start_iter, $text, 'span'); +        } +    } + +    sub getValue +    { +        my $self = shift; + +        (my $label = $self) =~ s/<.*?>(.*?)<\/.*?>/$1/g; +        return $label; +    } + +} + +{ +    package GCColorTable; +     +    use Glib::Object::Subclass +                Gtk2::Table:: +    ; +     +    @GCColorTable::ISA = ('Gtk2::Table', 'GCGraphicComponent', 'GCPseudoHistoryComponent'); + +    sub new +    { +        my ($proto, $columns, $labels, $headerStyle, $contentStyle, $topHeader) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new(1, $columns); + +        bless ($self, $class); + +        $self->set_col_spacings(3); +        $self->set_row_spacings(3); +        $self->{number} = $columns; +        $self->{style} = $contentStyle; +        $self->{firstRow} = 0; +        if ($topHeader) +        { +            $self->{firstRow}++; +            my $top = new GCColorLabel($headerStyle->{bgColor}); +            $top->set_padding($GCUtils::halfMargin,$GCUtils::halfMargin); +            $top->setMarkup('<span '.$headerStyle->{style}.'>'.$topHeader.'</span>'); +            $self->attach($top, 0, $columns, 0, 1, ['expand', 'fill'], 'fill', 0, 0);             +        } +        my $i; +        if ($columns > 1) +        { +            for $i(0..($columns - 1)) +            { +                my $header = new GCColorLabel($headerStyle->{bgColor}); +                $header->set_padding($GCUtils::halfMargin,$GCUtils::halfMargin); +                $header->setMarkup('<span '.$headerStyle->{style}.'>'.$labels->[$i].'</span>'); +                $self->attach($header, $i, $i + 1, $self->{firstRow}, $self->{firstRow} + 1, ['expand', 'fill'], 'fill', 0, 0); +            } +            $self->{firstRow}++; +        } +        $self->initHistory($columns); +        return $self; +    } + +    sub setValue +    { +        my ($self, $value) = @_; +        $self->{value} = $value; +  +        foreach (@{$self->{labels}}) +        { +            $self->remove($_); +            $_->destroy; +        } + +        $self->{labels} = []; +        my @lines; +        if (ref($value) eq 'ARRAY') +        { +            @lines = @$value; +        } +        else +        { +            $value =~ s/^\s*//; +            @lines = split m/,/, $value; +        } +        if ($#lines < 0) +        { +            $self->hide_all; +            return; +        } +        my $i = $self->{firstRow}; +        $self->resize($#lines + 1 + $self->{firstRow}, $self->{number}); +        foreach (@lines) +        { +            my @cols; +            if (ref($value) eq 'ARRAY') +            { +                @cols = @$_; +            } +            else +            { +                @cols = split m/;/, $_; +            } +            my $j = 0; +            for my $col(@cols) +            { +                # TODO Optimize GCColorLongLabel. It offers a better display (no scrollbar) +                # but it slows down the display. +                my $label = new GCColorLabel($self->{style}->{bgColor}); +                $label->set_padding($GCUtils::halfMargin,$GCUtils::halfMargin); +                #my $label = new GCColorLongLabel($self->{style}->{bgColor}, '2em'); +                $label->setMarkup('<span '.$self->{style}->{style}.'>'.$self->cleanMarkup($col, 1).'</span>'); +                $self->attach($label, $j, $j + 1, $i, $i + 1, ['expand', 'fill'], 'fill', 0, 0); +                push @{$self->{labels}}, $label; +                $j++; +            } +            $i++; +        } +        $self->show_all; +    } + +    sub getValue +    { +        my $self = shift; +        return $self->{value}; +    } +     +    sub setBgColor +    { +        my ($self, $color) = @_; +        return; +    } + +    sub set_justify +    { +        my ($self, $value) = @_; +        return; +    } +     +} + +{ +    package GCColorText; + +    use Glib::Object::Subclass +                Gtk2::ScrolledWindow:: +    ; +     +    @GCColorText::ISA = ('Gtk2::ScrolledWindow', 'GCGraphicComponent', 'GCPseudoHistoryComponent'); + +    sub new +    { +        my ($proto, $color, $height, $listType) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new; +        bless ($self, $class); + +        $listType = 0 if !$listType; +        $self->{text} = new Gtk2::TextView; +        $self->{text}->set_editable(0); +        $self->{text}->set_wrap_mode('word'); +        $self->{background} = $color; +        $self->{text}->modify_base('normal', $color); +        $self->{text}->modify_bg('normal', $color); +        $self->{text}->set_border_width($GCUtils::halfMargin); +        $self->set_border_width(0); +        $self->set_shadow_type('none'); +        $self->set_policy('automatic', 'automatic'); +        $self->add($self->{text}); +        $self->initHistory($listType); + +        my $layout = $self->create_pango_layout('G'); +        my (undef, $rect) = $layout->get_pixel_extents; +        $self->{em} = 1.5 * $rect->{height};         + +        $self->setHeight($height) if $height; + +        return $self; +    } +     +    sub acceptMarkup +    { +        my $self = shift; +        return 1; +    } + +    sub setMarkup +    { +        my ($self, $text) = @_; +         +        if ($self->{resize}) +        { +            $self->resize; +        } +        else +        { +            my $buffer = $self->{text}->get_buffer; +            $self->{text}->get_buffer->set_text(''); +            $text =~ s|<span ([^>]*?)>([^<]*?)</span>|$2|; +            if ($self->{spanTag}) +            { +                $buffer->get('tag-table')->remove($self->{spanTag}); +            } +            $self->{spanTag} = $buffer->create_tag('span', $self->getTagFromSpan($1)); +            $buffer->insert_with_tags_by_name ($buffer->get_start_iter, $text, 'span'); +        } +    } + +    sub getValue +    { +        my $self = shift; + +        (my $label = $self->{text}) =~ s/<.*?>(.*?)<\/.*?>/$1/g; +        return $label; +    } + +    sub setHeight +    { +        my ($self, $height) = @_; +        $height =~ s/^([0-9]+)em$/$1*$self->{em}/e; +        $self->set_size_request(-1, $height); +    } + +    sub AUTOLOAD +    { +        my $self = shift; +        my $name = our $AUTOLOAD; +        return if $name =~ /::DESTROY$/; +        $name =~ s/.*?::(.*)/$1/; +        $self->{text}->$name(@_); +    } +} + +{ +    package GCColorExpander; +     +    use Glib::Object::Subclass +                Gtk2::Expander:: +    ; +     +    @GCColorExpander::ISA = ('GCExpander'); + +    sub new +    { +        my ($proto, $label, $bgColor, $fgStyle) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new($label); + +        bless ($self, $class); +        $self->{label} = new GCColorLabel($bgColor); +        $self->set_label_widget($self->{label}); +        $self->{fgStyle} = $fgStyle; + +        return $self; +    } +     +    sub setValue +    { +        my ($self, $label, $description) = @_; + +        $label = '<span '.$self->{fgStyle}.">$label</span>"; + +        $self->{label}->set_markup($label); +        if ($description) +        { +            $self->{description}->set_label($description); +            $self->{description}->show; +        } +        else +        { +            $self->{description}->set_label(''); +            $self->{description}->hide; +        } + + +    } +} + +{ +    package GCDialogHeader; +     +    use Glib::Object::Subclass +                Gtk2::HBox:: +    ; +     +    @GCDialogHeader::ISA = ('Gtk2::HBox', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $text, $imageStock, $logosDir) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; + +        bless ($self, $class); +         +        $self->{label} = new Gtk2::Label; +        $self->{label}->set_markup("<span size='large' weight='bold'>$text</span>"); +        $self->{label}->set_alignment(0,0.5); +         +        if (-f $logosDir.$imageStock.'.png') +        { +            $self->{image} = Gtk2::Image->new_from_file($logosDir.$imageStock.'.png'); +            $self->pack_start($self->{image},0,1,5); +        } +         +        $self->pack_start($self->{label},0,1,5); +         +        return $self; +    } +} + +{ +    package GCImageBox; +     +    use Glib::Object::Subclass +                Gtk2::VBox:: +    ; +     +    @GCImageBox::ISA = ('Gtk2::VBox', 'GCGraphicComponent'); +     +    sub new_from_file +    { +        my ($proto, $imageFile, $label) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new(); + +        bless ($self, $class); + +        my $image = Gtk2::Image->new_from_file($imageFile); +        $self->init($image, $label); +         +        return $self; +    } +    sub new_from_stock +    { +        my ($proto, $stockId, $label) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new(); + +        bless ($self, $class); + +        my $image = Gtk2::Image->new_from_stock($stockId, 'large-toolbar'); +        $self->init($image, $label); +          +        return $self;         +    } +     +    sub init +    { +        my ($self, $image, $label) = @_; +         +        $self->{label} = new Gtk2::Label($label); +         +        $self->pack_start($image, 0, 0, 0); +        $self->pack_start($self->{label}, 0, 0, 0); +         +        $self->show_all; +    } +} + +{ +    package GCGroup; + +    use Glib::Object::Subclass +                Gtk2::Frame:: +    ; +     +    @GCGroup::ISA = ('Gtk2::Frame', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $title) = @_; +        my $class = ref($proto) || $proto; +        my $self  = $class->SUPER::new; + +        bless ($self, $class); + +        $self->set_shadow_type('none'); +        $self->set_border_width($GCUtils::margin); +        $self->{label} = new Gtk2::Label; +        $self->{label}->set_padding(0,0); +        #$label->set_border_width(0); +        $self->setLabel($title); +        $self->set_label_widget($self->{label}); +        $self->set_label_align(0,0); + +        $self->{marginBox} = new Gtk2::HBox(0,0); +        $self->{marginBox}->set_border_width($GCUtils::halfMargin); +        $self->add($self->{marginBox}); + +        return $self;         +    } +     +    sub setLabel +    { +        my ($self, $label) = @_; +         +        $self->{label}->set_markup('<b>'.$label.'</b>'); +    } +     +    sub addWidget +    { +        my ($self, $widget, $margin) = @_; +        $margin = $GCUtils::halfMargin if !$margin; +        $self->{marginBox}->pack_start($widget, 1, 1, $margin); +    } +     +    sub setPadding +    { +        my ($self, $value) = @_; +         +        $self->{marginBox}->set_border_width($value); +    } +} + +{ +    package GCExpander; +     +    use Glib::Object::Subclass +                Gtk2::Expander:: +    ; +     +    @GCExpander::ISA = ('Gtk2::Expander', 'GCGraphicComponent'); +     +    sub new +    { +        my ($proto, $label, $bold) = @_; +        my $class = ref($proto) || $proto; +     +        my $self = $class->SUPER::new($label); + +        bless ($self, $class); +        $self->{hbox} = new Gtk2::HBox(0,0); +        $self->{label} = new Gtk2::Label($label); +        $self->{label}->set_alignment(0,0.5); +        $self->{label}->set_markup("<b>$label</b>") +            if $bold; +        $self->{description} = new Gtk2::Label; +        $self->{description}->set_alignment(0,0); +        eval {$self->{description}->set_line_wrap_mode('word');}; +        $self->{hbox}->pack_start($self->{label}, 0, 0, 0); +        $self->{hbox}->pack_start($self->{description}, 1, 1, 0); +        $self->set_label_widget($self->{hbox}); +        $self->{signalHandler} = undef; +        return $self; +    } + +    sub setMode +    { +        my ($self, $mode) = @_; +        if ($mode eq 'asis') +        { +            $self->{description}->set_ellipsize('none'); +            $self->{description}->set_line_wrap(0); +            if ($self->{signalHandler}) +            { +                $self->signal_handler_disconnect($self->{signalHandler}); +                $self->{signalHandler} = undef; +                $self->{description}->set_size_request(-1, -1); +            } +        } +        else +        { +            $self->{signalHandler} = $self->signal_connect('size-allocate' => sub { +                my $width = $self->allocation->width  +                          - $self->{label}->allocation->width +                          - ( 4 * $GCUtils::margin); +                $self->{description}->set_size_request(($width >= -1) ? $width : -1 , -1); +                return 0; +            }); +            if ($mode eq 'wrap') +            { +                $self->{description}->set_ellipsize('none'); +                $self->{description}->set_line_wrap(1); +            } +            else +            { +                $self->{description}->set_ellipsize('end'); +                $self->{description}->set_line_wrap(0); +            } +        } +    } + +    sub setValue +    { +        my ($self, $label, $description) = @_; + +        $self->{label}->set_label($label); +        if ($description) +        { +            $self->{description}->set_label($description); +            $self->{description}->show; +        } +        else +        { +            $self->{description}->set_label(''); +            $self->{description}->hide; +        } +    } +} + +{ +    package GCFieldSelector; + +    use Glib::Object::Subclass +                Gtk2::ComboBox:: +    ; +     +    @GCFieldSelector::ISA = ('GCMenuList'); +    our $anyFieldValue = 'GCAnyField'; +     +    sub valueToDisplayed +    { +        my ($self, $value) = @_; +        return ''; +    } +     +    sub setValue +    { +        my ($self, $value) = @_; + +        $self->{listModel}->foreach(sub { +            my ($model, $path, $iter) = @_; +            if ($model->get_value($iter, 0) eq $value) +            { +                $self->set_active_iter($iter); +                return 1; +            } +            return 0; +        }); +    } +     +    sub getValues +    { +        my $self = shift; +        return []; +    } +     +    sub setValues +    { +        my ($self, $values, $separatorPosition, $preserveValue) = @_; +        return; +    } +     +    sub new +    { +        my ($proto, $withImages, $model, $advancedFilter, $withAnyField, $withoutEmpty, $uniqueType) = @_; +        my $class = ref($proto) || $proto; +        my $self = Gtk2::ComboBox->new; +        bless($self, $class); +        $self->{withImages} = $withImages; +        $self->{advancedFilter} = $advancedFilter; +        $self->{withAnyField} = $withAnyField; +        $self->{withoutEmpty} = $withoutEmpty; +        $self->{uniqueType} = $uniqueType; +         +        $self->{listModel} = Gtk2::TreeStore->new('Glib::String', 'Glib::String'); +        $self->set_model($self->{listModel}); +        my $renderer = Gtk2::CellRendererText->new; +        $self->pack_start($renderer, 1); +        $self->add_attribute($renderer, text => 1); + +        $self->set_cell_data_func($renderer, sub { +            my ($layout, $cell, $model, $iter) = @_; +            my $sensitive = !$model->iter_has_child($iter); +            $cell->set('sensitive', $sensitive); +        }); + +        $self->{default} = 0; +        $self->set_focus_on_click(1); +         +        $self->setModel($model) if $model; + +        return $self; +    } + +    sub setModel +    { +        my ($self, $model) = @_; + +        $self->{listModel}->clear; +        my $field; +        my @fieldsInfo = @{$model->getDisplayedInfo}; +         +        if (! $self->{withoutEmpty}) +        { +            $self->{listModel}->set($self->{listModel}->append(undef), +                                0 => '', +                                1 => ''); +        } +        if ($self->{withAnyField}) +        { +            $self->{listModel}->set($self->{listModel}->append(undef), +                                0 => $anyFieldValue, +                                1 => $model->getDisplayedText('AdvancedSearchAnyField')); +        } +         +        foreach my $group(@fieldsInfo) +        { +            my @fields; +            foreach $field (@{$group->{items}}) +            { +                my $id = $field->{id}; +                next if ($model->{fieldsInfo}->{$id}->{type} eq 'image') && (!$self->{withImages}); +                next if ($self->{advancedFilter} +                      && ( +                             ($id eq $model->{commonFields}->{id}) +                          || ($id eq $model->{commonFields}->{title}) +                          || ($id eq $model->{commonFields}->{url}) +                         ) +                        ); +                next if ! $field->{label}; +                next if $self->{uniqueType} && ($self->{uniqueType} ne $model->{fieldsInfo}->{$id}->{type}); +                push @fields, $field; +            } +            if (scalar @fields) +            { +                my $groupIter = $self->{listModel}->append(undef); +                $self->{listModel}->set($groupIter, +                                    0 => undef, +                                    1 => $group->{title}); +                foreach $field(sort {$a->{label} cmp $b->{label}} @fields) +                { +                    my $fieldIter = $self->{listModel}->append($groupIter); +                    $self->{listModel}->set($fieldIter, +                                        0 => $field->{id}, +                                        1 => $field->{label}); +                } +            } +        } +        $self->{model} = $model; +     } +    sub activateStateTracking +    { +        my $self = shift; +    } + +    # Create a widget suitable to enter a value according to current field type +    # It will destroy the current widget if it exists +    sub createEntryWidget +    { +        # $parent is the class that contains needed information +        # $comparison is the kind of comparison. Mainly useful if it is 'range' to create 2 fields +        # $currentWidget is the one we are replacing +        my ($self, $parent, $comparison, $currentWidget) = @_; +        my $value; +        if ($currentWidget) +        { +            $value = $currentWidget->getValue; +            $currentWidget->destroy; +        } +        my $widget; +        my $field = $self->getValue; +        my $info = $self->{model}->{fieldsInfo}->{$field}; + +        # These ones are required for createWidget +        $self->{lang} = $parent->{lang}; +        $self->{options} = $parent->{options}; +        $self->{window} = $parent; + +        ($widget, undef) = GCBaseWidgets::createWidget($self, $info, $comparison); + +        if ($info->{type} eq 'history text') +        { +            $widget->setValues($parent->{parent}->{panel}->getValues($field)); +        } +        elsif ($info->{type} eq 'single list') +        { +            $widget->setValues($parent->{parent}->{panel}->getValues($field)->[0]);             +        } +        $widget->setValue($value); + +        return $widget; +    } +} + +{ +    package GCComparisonSelector; + +    use Glib::Object::Subclass +                Gtk2::ComboBox:: +    ; +     +    @GCComparisonSelector::ISA = ('GCMenuList'); + +    sub new +    { +        my ($proto, $parent) = @_; +        my $class = ref($proto) || $proto; +        my $self = $class->SUPER::new; +        bless($self, $class); +        $self->setValues([ +                          {value => 'contain', displayed => $parent->{lang}->{ModelFilterContain}}, +                          {value => 'notcontain', displayed => $parent->{lang}->{ModelFilterDoesNotContain}}, +                          {value => 'regexp', displayed => $parent->{lang}->{ModelFilterRegexp}}, +                          {value => 'range', displayed => $parent->{lang}->{ModelFilterRange}}, +                          {value => 'eq', displayed => '='}, +                          {value => 'lt', displayed => '<'}, +                          {value => 'le', displayed => '≤'}, +                          {value => 'gt', displayed => '>'}, +                          {value => 'ge', displayed => '≥'}, +                        ]); +        return $self; +     } +} + +1;  | 
