
######################################################################
## $Id: Compound.pm 3668 2006-03-11 20:51:13Z spadkins $
######################################################################

package App::Widget::Compound;
$VERSION = (q$Revision: 3668 $ =~ /(\d[\d\.]*)/)[0];  # VERSION numbers generated by svn

use App::Widget;
@ISA = ( "App::Widget" );

use strict;

=head1 NAME

App::Widget::Compound - A non-visual compound of other (presumably) visual widgets

=head1 SYNOPSIS

 $conf = {
   SessionObject => {
     begin_datetime => {
       class  => "App::Widget::Compound",
       components => [ "begin_yr", "begin_mon", "begin_day", "begin_hr", "begin_min", "begin_sec", ],
       parse  => "^([0-9]{4})-([0-9][0-9])-([0-9][0-9]) ([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$",
       format => "%04d-%02d-%02d %02d:%02d:%02d",
     }
     end_datetime => {
       class  => "App::Widget::Compound",
       components => [ "end_yr", "end_mon", "end_day", ],
       separator  => "-",
     }
   },
 };

=cut

######################################################################
# CONSTANTS
######################################################################

######################################################################
# ATTRIBUTES
######################################################################

# INPUTS FROM THE ENVIRONMENT

=head1 DESCRIPTION

This class is a non-visual compound of other (presumably) visual widgets.
However, it must be "displayed" so that event-html is created 
(an <input type="hidden"> tag).

The role of a Compound widget is to keep the joined compound value
in sync with the split component values.  Thus, when the form is
submitted an event is fired to join() the component values into
the compound value. 

Processing then occurs, presumably affecting
the value of the compound widget directly.

When the compound widget is "displayed" with an html() call, its
value is split out into the component values.  This should happen
before the component widgets are displayed or the wrong values
will be shown.  This may be done by placing the compound widget
higher on the page than its components or by making an explicit
call to split() in the code.

=cut

######################################################################
# CONSTRUCTOR
######################################################################

sub _init {
    my $self = shift;
    $self->SUPER::_init(@_);
    my $name = $self->{name};
    my $context = $self->{context};

    my $component_widgets = $self->{components};
    if ($component_widgets && $#$component_widgets > -1) {
        foreach my $widget (@$component_widgets) {
            $context->widget($widget);
        }
    }
}

######################################################################
# EVENTS
######################################################################

# Usage: $widget->handle_event($name, $event, @args);
sub handle_event {
    my ($self, $name, $event, @args) = @_;

    if ($event eq "split") {
        $self->split(@_);
        return 1;
    }
    elsif ($event eq "join") {
        $self->join(@_);
        return 1;
    }
    elsif ($event eq "change") {   # i.e. onChange
        $self->change(@_);
        return 1;
    }
    else {
        return $self->SUPER::handle_event(@_);
    }
}

sub split {
    my $self = shift;
    my $context = $self->{context};
    my $name = $self->{name};
    my $value  = $context->so_get($name);
    return if (! defined $value || $value eq "");

    my (@values);

    my $parse = $self->{parse};
    if ($parse) {
        @values = ($value =~ /$parse/);
    }
    else {
        my $sep = $self->{separator};
        $sep = "," if (!defined $sep);
        @values = split(/$sep/, $value);
    }

    my $component_widgets = $self->{components};
    if ($component_widgets && $#$component_widgets > -1) {
        for (my $i = 0; $i <= $#$component_widgets; $i++) {
            $context->so_set($component_widgets->[$i], undef, $values[$i]);
        }
    }
}

sub join {
    my $self = shift;
    my $context = $self->{context};
    my $name = $self->{name};

    my (@values, $value);

    my $component_widgets = $self->{components};
    if ($component_widgets && $#$component_widgets > -1) {
        for (my $i = 0; $i <= $#$component_widgets; $i++) {
            $values[$i] = $context->so_get($component_widgets->[$i]);
        }
    }

    my $format = $self->{format};
    if ($format) {
        $value = sprintf($format, @values);
    }
    else {
        my $sep = $self->{separator};
        $sep = "," if (!defined $sep);
        $value = join($sep, @values);
    }

    $context->so_set($name, undef, $value);
}

sub change {
    my $self = shift;
    $self->join();
}

######################################################################
# OUTPUT METHODS
######################################################################

sub html {
    my $self = shift;

    $self->split();

    return $self->callback_event_tag("change");
}

1;

