The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package Tk::Wizard::Choices;

use strict;
use warnings;
use warnings::register;

use vars '$VERSION';
$VERSION = do { my @r = ( q$Revision: 2.77 $ =~ /\d+/g ); sprintf "%d." . "%03d" x $#r, @r };

use Carp ();

# use Scalar::Util qw( reftype );

=head1 NAME

Tk::Wizard::Choices - C<Tk::Wizard> pages to collect end-user choices

=head1 SYNOPSIS

Currently automatically loaded by C<Tk::Wizard>, though this
behaviour is deprecated and is expected to change in 2008.

=head1 DESCRIPTION

Adds a number of methods to C<Tk::Wizard>, to collect choices made
by the end-user.

=head1 METHODS

=head2 addMultipleChoicePage

Allow the user to make multiple choices among several options:
each choice sets a variable passed as reference to this method.

Accepts the usual parameters plus:

=over 4

=item -relief

For the checkbox buttons - see L<Tk::options>.

=item -choices

A reference to an array of hashes with the following fields:

=over 4

=item -title

Title of the option, will be rendered in bold

=item -subtitle

Text rendered smaller beneath the title

=item -variable

Reference to a variable that will contain the result of the choice.
Croaks if none supplied.  Your -variable will contain the default
L<Tk::Checkbutton|Tk::Checkbutton> values of 1 for checked and 0 for
unchecked.

=item -checked

Pass a true value to specify that the box should initially
appear checked.

=back

Here is an example of what the -choices parameter should look like:

  $wizard->addMultipleChoicePage(
    -title => "Another toy example",
    -choices =>
      [
        {
         -title => 'choice 1',
         -variable => \$choice1,
        },
        {
         -title => 'choice 2, default is checked',
         -variable => \$choice2,
         -checked => 1,
        },
      ],
    );

=back

=cut

sub Tk::Wizard::addMultipleChoicePage {
    my $self = shift;
    my $args = {@_};
    # return $self->addPage( sub { $self->_page_multiple_choice($args) } );

	my %btn_args =
		map { my $x = delete $args->{$_}; $_ => $x }
		grep { /ButtonAction$/ }
		keys %$args;
	return $self->addPage( sub { $self->_page_multiple_choice($args) }, %btn_args );
}


sub Tk::Wizard::_page_multiple_choice {
    my $self  = shift;
    my $args  = shift;
    my $frame = $self->blank_frame(%$args);
    if ( !ref( $args->{-choices} ) || ( ref( $args->{-choices} ) ne 'ARRAY' ) ) {
        Carp::croak "-choices should be a ref to an array!";
    }

    my $content = $frame->Frame( -background => $self->{background}, )->pack(
        -side   => 'top',
        -anchor => "n",
        -padx   => 10,
        -pady   => 10,
    );

    foreach my $opt ( @{ $args->{-choices} } ) {
        Carp::croak "Option in -choices array is not a hash!" if not ref $opt or ref $opt ne 'HASH';
        Carp::croak "No -variable!"                    if not $opt->{-variable};
        Carp::croak "-variable should be a reference!" if not ref $opt->{-variable};

        my $b = $content->Checkbutton(
            -text             => $opt->{-title},
            -justify          => 'left',
            -relief           => $args->{-relief} || 'flat',
            -font             => "RADIO_BOLD",
            -variable         => $opt->{-variable},
            -background       => $self->{background},
            -activebackground => $self->{background},
        )->pack(qw/-side top -anchor w /);

        $b->invoke if $opt->{-checked};

        my $s = $opt->{-subtitle} || '';

        # Seven spaces indentation is perfect with my Windows XP
        # default font:
        $s =~ s!(^|\n)!$1       !g;
        my $l = $content->Label(
            -font       => $self->{defaultFont},
            -text       => $s,
            -anchor     => 'w',
            -justify    => 'left',
            -background => $self->{background},
        )->pack(qw/-side top -anchor w/);

        # DEBUG_FRAME && $l->configure( -background => 'light blue' );
    }

    return $frame;
}


=head2 addSingleChoicePage

Allow the user to make one choice from among several options
(i.e. a group of radio buttons).
Each choice sets a variable passed as reference to this method.

Accepts the usual parameters plus:

=over 4

=item -relief

For the radio buttons - see L<Tk::options>.

=item -variable

Reference to a variable that will contain the result of the choice.
Croaks if none supplied.  Your -variable will contain the -value of the
radio button that is selected when the user clicks "Next".

=item -choices

A reference to an array of hashes with the following fields:

=over 4

=item -title

Title of the option, will be rendered in bold

=item -subtitle

Text rendered smaller beneath the title

=item -value

This value will be placed in your -variable variable if this button is
selected

=item -selected

Pass a true value to specify that this radio should initially appear
selected.  If none of the choices have -selected, then the first
choice will be selected by default.

=back

Here is an example of what the -choices parameter should look like:

  $wizard->addSingleChoicePage(
    -title => 'Another toy example',
    -text => 'Choose one of the following:',
    -variable => \$choice,
    -choices =>
      [
        {
         -title => 'choice 1',
         -value => 1,
        },
        {
         -title => 'choice two, default this one selected',
         -value => 'two',
         -selected => 1,
        },
      ],
    );

=back

=cut

sub Tk::Wizard::addSingleChoicePage {
    my $self = shift;

    # We have to make a copy of our args in order for them to get
    # "saved" in this coderef:
    my $args = {@_};

    # return $self->addPage( sub { $self->_page_single_choice($args) } );
	my %btn_args =
		map { my $x = delete $args->{$_}; $_ => $x }
		grep { /ButtonAction$/ }
		keys %$args;
	return $self->addPage( sub { $self->_page_single_choice($args) }, %btn_args );
}

sub Tk::Wizard::_page_single_choice {
    my $self = shift;
    my $args = shift;
    my $not_first_page = 0;

    if (not defined( $args->{-choices} ) ) {
        Carp::croak "-choices argument missing";
    }
    if (not ref( $args->{-choices} ) or ( ref( $args->{-choices} ) ne 'ARRAY' ) ) {
        Carp::croak "-choices must be a ref to an array!";
    }
    Carp::croak "-variable argument missing"     if !defined( $args->{-variable} );
    Carp::croak "-variable must be a reference!" if !ref $args->{-variable};

    # Take care of the -title, -text, etc.:
    my $frame = $self->blank_frame(%$args);
    my $content = $frame->Frame( -background => $self->{background}, )->pack(
        -side   => 'top',
        -anchor => "n",
        -padx   => 10,
        -pady   => 10,
    );

    foreach my $opt ( @{ $args->{-choices} } ) {
        if ( ref $opt ne 'HASH' ) {
            Carp::croak "Option in -choices array must be a hash";
        }
        $opt->{-title} ||= '';
        my $sValue = defined($opt->{-value}) ? $opt->{-value} : $opt->{-title};
        my $b = $content->Radiobutton(
            -text             => $opt->{-title},
            -justify          => 'left',
            -relief           => $args->{-relief} || 'flat',
            -font             => "RADIO_BOLD",
            -variable         => $args->{-variable},
            -value            => $sValue,
            -background       => $self->{background},
            -activebackground => $self->{background},
        )->pack(qw/-side top -anchor w /);

        ${ $args->{-variable} } ||= $sValue if not $not_first_page++;
        ${ $args->{-variable} } = $sValue if $opt->{-selected};

        my $s = $opt->{-subtitle} || '';

		# Seven spaces indentation is perfect with my Windows XP default font:
        if ( $s ne '' ) {
            $s =~ s!(^|\n)!$1       !g;
            my $l = $content->Label(
                -font       => $self->{defaultFont},
                -text       => $s,
                -anchor     => 'w',
                -justify    => 'left',
                -background => $self->{background},
            )->pack(qw/-side top -anchor w/);
        }
    }

    return $frame;
}

1;

=head1 AUTHOR

Lee Goddard (lgoddard@cpan.org).

=head1 COPYRIGHT

Copyright (C) Lee Goddard, 11/2002 - 01/2008 ff.

Made available under the same terms as Perl itself.