# combo styles
package
    cs;
use constant Simple       =>  0;
use constant DropDown     =>  1;
use constant DropDownList =>  2;

use strict;
use warnings;

package Prima::ComboBox;

use vars qw(@ISA %listProps %editProps %listDynas $capture_mode);
use Prima qw( InputLine Lists Utils StdBitmap);
@ISA = qw(Prima::Widget);

use constant DefButtonX => 17;

# these are properties, methods and dynas of encapsulated widgets, edit and list

%editProps = map { $_ => 1 } qw(
	alignment      autoScroll  text         select_all
	charOffset     maxLen      insertMode   firstChar
	selection      selStart    selEnd       writeOnly
	copy           cut         delete       paste
	wordDelimiters readOnly    passwordChar focus
);

%listProps = map { $_ => 1 } qw(
	focusedItem hScroll
	integralHeight items       itemHeight
	topItem        vScroll     gridColor
	multiColumn    offset      autoHScroll
	autoVScroll
);

%listDynas = map { $_ => 1} qw(onDrawItem onSelectItem);

for ( keys %editProps) {
	eval <<GENPROC;
sub $_ { return shift-> {edit}-> $_(\@_); }
sub Prima::ComboBox::DummyEdit::$_ {}
GENPROC
}

for (keys %listProps) {
	eval <<GENPROC;
sub $_ { return shift-> {list}-> $_(\@_); }
sub Prima::ComboBox::DummyList::$_ {}
GENPROC
}

$capture_mode = (Prima::Application-> get_system_info-> {apc} == apc::Unix);

sub profile_default
{
	my $f = $_[ 0]-> get_default_font;
	return {
		%{Prima::InputLine-> profile_default},
		%{Prima::ListBox-> profile_default},
		%{$_[ 0]-> SUPER::profile_default},
		style          => cs::Simple,
		caseSensitive  => 0,
		autoHeight     => 1,

		autoHScroll    => 0,
		autoVScroll    => 1,
		listVisible    => 0,
		editHeight     => $f-> {height} + 2,
		listHeight     => $::application-> uiScaling * 100,
		ownerBackColor => 1,
		selectable     => 0,
		literal        => 1,
		scaleChildren  => 0,
		autoEnableChildren => 1,

		editClass      => 'Prima::InputLine',
		listClass      => 'Prima::ListBox',
		buttonClass    => 'Prima::Widget',
		scrollBarClass => 'Prima::ScrollBar',
		editProfile    => {},
		listProfile    => {},
		buttonProfile  => {},
		hScrollBarProfile => {},
		vScrollBarProfile => {},
		listDelegations   => [qw(Leave SelectItem MouseUp Click KeyDown)],
		editDelegations   => [qw(FontChanged Create Setup KeyDown KeyUp Change Leave MouseWheel)],
		buttonDelegations => [qw(ColorChanged FontChanged MouseDown MouseClick
			MouseUp MouseMove MouseEnter MouseLeave Paint Enable Disable)],
	}
}

sub profile_check_in
{
	my ( $self, $p, $default) = @_;
	$self-> SUPER::profile_check_in( $p, $default);
	my $style = exists $p-> {style} ? $p-> {style} : $default-> {style};
	$p-> {autoHeight} = 0
		if exists $p-> {height} || exists $p-> {size} || exists $p-> {rect} || ( exists $p-> {top} && exists $p-> {bottom});
	if ( $style != cs::Simple) {
		$p-> { editHeight } = exists $p-> {height} ? $p-> {height} : $default-> {height}
			unless exists $p-> { editHeight };
	} else {
		my $fh = exists $p-> {font}-> {height} ?
			$p-> {font}-> {height} :
			$default-> {font}-> {height};
		$p-> { editHeight } = $fh + 2
			unless exists $p-> {editHeight };
	}
	$p-> {autoHScroll} = 0 if exists $p-> {hScroll};
	$p-> {autoVScroll} = 0 if exists $p-> {vScroll};
}

sub init
{
	my $self = shift;
	my %profile = @_;
	my $visible = $profile{visible};
	$profile{visible} = 0;
	$self-> {edit} = bless [], q\Prima::ComboBox::DummyEdit\;
	$self-> {list} = bless [], q\Prima::ComboBox::DummyList\;
	%profile = $self-> SUPER::init(%profile);
	my ( $w, $h) = ( $self-> size);
	$self-> {style}        = $profile{style};
	$self-> {listVisible}  = $profile{style} != cs::Simple;
	$self-> {caseSensitive}= $profile{caseSensitive};
	$self-> {literal}      = $profile{literal};
	my $eh = $self-> {editHeight } = $profile{editHeight };
	$self-> {listHeight}   = $profile{listHeight};
	$self-> {autoHeight}   = $profile{autoHeight};

	$self-> {edit} = $self-> insert( $profile{editClass} =>
		name        => 'InputLine',
		origin      => [ 0, $h - $eh],
		size        => [ $w - (( $self-> {style} == cs::Simple) ? 0 : ( $::application-> uiScaling * DefButtonX)), $eh],
		growMode    => gm::GrowHiX | gm::GrowLoY,
		selectable  => 1,
		tabStop     => 1,
		current     => 1,
		dndAware    => (( $self->{style} == cs::DropDown) ? 'Text' : 0 ),
		delegations => $profile{editDelegations},
		(map { $_   => $profile{$_}} keys %editProps),
		%{$profile{editProfile}},
	);

	if ( $self->{autoHeight} && $self->{style} != cs::Simple) {
		$self->check_auto_size;
		( $w, $h) = ( $self-> size);
		$eh = $self-> geomHeight;
		$self->{edit}->set( height => $eh, bottom => $h - $eh );
	}

	my %lp = (%listProps, scrollBarClass => 1, hScrollBarProfile => 1, vScrollBarProfile => 1);
	delete $lp{hScroll} if $profile{autoHScroll};
	delete $lp{vScroll} if $profile{autoVScroll};
	$self-> {list} = $self-> insert( $profile{listClass} =>
		name         => 'List',
		origin       => [ 0, 0],
		selectable   => $capture_mode ? ($self->{style} == cs::Simple) : 1,
		width        => $w,
		height       => ( $self-> {style} == cs::Simple) ? ( $h - $eh) : $self-> {listHeight},
		growMode     => gm::Client,
		tabStop      => 0,
		multiSelect  => 0,
		clipOwner    => $self-> {style} == cs::Simple,
		visible      => $self-> {style} == cs::Simple,
		delegations  => $profile{listDelegations},
		(map { $_    => $profile{$_}} grep { exists $profile{$_} ? 1 : 0} keys %listDynas),
		(map { $_    => $profile{$_}} keys %lp),
		%{$profile{listProfile}},
	);

	$self-> {button} = $self-> insert( $profile{buttonClass} =>
		ownerBackColor => 1,
		name           => 'Button',
		origin         => [ $w - $::application-> uiScaling * DefButtonX, $h - $eh],
		size           => [ $::application-> uiScaling * DefButtonX, $eh],
		visible        => $self-> {style} != cs::Simple,
		growMode       => gm::GrowLoX | gm::GrowLoY,
		tabStop        => 0,
		ownerFont      => 0,
		selectable     => 0,
		delegations    => $profile{buttonDelegations},
		%{$profile{buttonProfile}},
	);

	$self-> visible( $visible);
	return %profile;
}

sub check_auto_size
{
	my $self = $_[0];
	$self-> geomHeight( $self-> {edit}-> default_geom_height )
		if $self-> {autoHeight} && $self->{style} != cs::Simple && $self->{edit}->can('default_geom_height');
}

sub on_create
{
	my $self = $_[0];
	$self-> InputLine_Change( $self-> {edit})
		if $self-> {style} == cs::DropDownList;
}

sub on_size { $_[0]-> listVisible(0); }
sub on_move { $_[0]-> listVisible(0); }
sub on_hide { $_[0]-> listVisible(0); }

sub List_Leave
{
	$_[0]-> listVisible( 0) if $_[0]-> {style} != cs::Simple;
}

sub List_SelectItem
{
	return if defined $_[1]-> {interaction};
	$_[0]-> {edit}-> {interaction} = 1;
	$_[0]-> {edit}-> text($_[1]-> get_item_text( $_[1]-> focusedItem));
	$_[0]-> {edit}-> {interaction} = undef;
	$_[0]-> notify( q(Change)) if $_[0]-> {style} == cs::Simple;
}

sub List_MouseUp
{
	return unless $_[2] == mb::Left || $_[1]-> capture;
	$_[0]-> listVisible(0) if $_[0]-> {style} != cs::Simple;
	$_[0]-> notify( q(Change)) if $_[0]-> {style} != cs::Simple;
}

sub List_Click
{
	my ( $self, $list) = @_;
	$self-> {edit}-> {interaction} = 1;
	$self-> {edit}-> text( $list-> get_item_text( $list-> focusedItem));
	$self-> {edit}-> {interaction} = undef;
	$self-> listVisible(0);
	$self-> notify( q(Change));
}

sub List_KeyDown
{
	my ( $self, $list, $code, $key, $mod) = @_;
	if ( $key == kb::Esc) {
		$self-> listVisible(0);
		$list-> clear_event;
	} elsif ( $key == kb::Enter) {
		$list-> notify(q(Click));
		$list-> clear_event;
	}
}

sub Button_ColorChanged
{
	my $self = shift;
	if ( $self-> {style} != cs::Simple) {
		$self-> {list}-> color( $self-> {button}-> color);
		$self-> {list}-> backColor( $self-> {button}-> backColor);
	}
}

sub Button_FontChanged
{
	my $self = shift;
	if ( $self-> {style} != cs::Simple) {
		my $f = $self-> {button}-> font;
		$self-> {list}-> font($f);
	}
}

sub Button_MouseDown
{
	$_[0]-> listVisible( !$_[0]-> {list}-> visible);
	$_[1]-> clear_event;
	return if !$_[0]-> {list}-> visible;
	$_[1]-> capture(1);
}

sub Button_MouseClick
{
	return unless $_[-1];
	$_[0]-> listVisible( !$_[0]-> {list}-> visible);
	$_[1]-> clear_event;
	return if !$_[0]-> {list}-> visible;
	$_[1]-> capture(1);
}


sub Button_MouseMove
{
	$_[1]-> clear_event;
	if ($_[1]-> capture) {
		my ($x,$y,$W,$H) = ($_[3],$_[4],$_[1]-> size);
		return unless ($x < 0) || ($y < 0) || ($x >= $W) || ($y >= $H);
		$_[1]-> capture(0);
		$_[0]-> {list}-> mouse_down( mb::Left, 0, 5, $_[0]-> {list}-> height - 5, 1)
			if ($_[0]-> {list}-> visible);
	}
}

sub Button_MouseEnter
{
	my ( $self, $button ) = @_;
	if ( !$button->capture && $self->enabled) {
		$button->{prelight} = 1;
		$button->repaint;
	}
}

sub Button_MouseLeave
{
	my ( $self, $button ) = @_;
	if ( !$button->capture && $button->{prelight}) {
		delete $button->{prelight};
		$button->repaint;
	}

}

sub Button_MouseUp { $_[1]-> capture(0); }

sub Button_Paint
{
	my ( $owner, $self, $canvas) = @_;
	my ( $w, $h)   = $canvas-> size;
	my $ena    = $self-> enabled;
	my @clr    = $ena ?
		( $self-> color, $self-> backColor) :
		( $self-> disabledColor, $self-> disabledBackColor);
	$clr[1] = $self->prelight_color($clr[1]) if $self->{prelight};
	my $lv = $owner-> listVisible;
	my ( $rc, $lc) = ( $self-> light3DColor, $self-> dark3DColor);
	( $rc, $lc) = ( $lc, $rc) if $lv;
	$canvas-> rect3d( 0, 0, $w-1, $h-1, 1, $rc, $lc, $clr[1]);
	if ( $ena) {
		$canvas-> color( $rc);
		$canvas-> fillpoly([ 5, $h * 0.6 - 1, $w - 4, $h * 0.6 - 1, $w/2 + 1, $h * 0.4 - 1]);
	}
	$canvas-> color( $clr[0]);
	$canvas-> fillpoly([ 4, $h * 0.6, $w - 5, $h * 0.6, $w/2, $h * 0.4]);
}

sub Button_Enable  { $_[1]-> repaint }
sub Button_Disable { $_[1]-> repaint }

sub InputLine_Leave
{
	$_[0]-> listVisible( 0) if $capture_mode and $_[0]-> {style} != cs::Simple;
}

sub InputLine_FontChanged
{
	$_[0]-> editHeight ( $_[1]-> default_geom_height );
	$_[0]-> check_auto_size;
}

sub InputLine_Create
{
	$_[1]-> {incline} = '';
}

sub InputLine_KeyDown
{
	my ( $self, $edit, $code, $key, $mod, $repeat) = @_;

	if ( $self-> listVisible) {
		$self-> {list}-> notify(q(KeyDown), $code, $key, $mod, $repeat);
		$edit-> clear_event;
		return;
	}

	return if $mod & km::DeadKey;
	if (
		( $key & 0xFF00) &&
		( $key != kb::NoKey) &&
		( $key != kb::Space) &&
		( $key != kb::Backspace)
	) {
		return if $key == kb::Tab || $key == kb::BackTab || $key == kb::NoKey;
		$edit-> {incline} = '';
		$self-> listVisible(1), $edit-> clear_event
			if $key == kb::Down && $_[0]-> {style} != cs::Simple;
		$_[0]-> notify( q(Change)), $edit-> clear_event
			if $key == kb::Enter && $_[0]-> {style} == cs::DropDownList;
		return;
	} else {
		return unless $code;
		return unless $_[0]-> {literal};
		return if $_[0]-> {style} != cs::DropDownList;
		return if $mod & ( km::Alt|km::Ctrl);
		$edit-> {keyDown} = 1;
		if ( $key == kb::Backspace) {
			chop $edit-> {incline};
		} else {
			$code = chr ( $code);
			$code = uc $code unless $self-> caseSensitive;
			$edit-> {incline} .= $code;
		}
		my ($ftc,$i,$txt,$t);
		$ftc = quotemeta $edit-> {incline};
		for ( $i = 0; $i < $_[0]-> {list}-> count; $i++) {
			$txt = $_[0]-> {list}-> get_item_text($i);
			$t = $txt;
			$t = uc $t unless $self-> caseSensitive;
			last if $t =~ /^$ftc/;
		}
		if ( $i < $_[0]-> {list}-> count) {
			$edit-> text( $txt);
		} else {
			chop $edit-> {incline};
		}
		$edit-> selection( 0, length $edit-> {incline});
	}
	$edit-> clear_event if $_[0]-> {style} == cs::DropDownList;
}


sub InputLine_KeyUp
{
	$_[1]-> {keyDown} = undef;
}

sub InputLine_Setup
{
	my $self = shift;
	$self-> InputLine_Change(@_) if $self-> {style} == cs::DropDownList;
}

sub InputLine_Change
{
	return if defined $_[0]-> {edit}-> {interaction};
	return unless $_[0]-> {literal};

	$_[0]-> notify(q(Change)) if $_[0]-> {style} != cs::DropDownList;

	$_[0]-> {list}-> {interaction} = 1;
	my ( $self, $style, $list, $edit) = ($_[0], $_[0]-> {style}, $_[0]-> {list}, $_[1]);
	my $i;
	my $found = 0;
	my $cap = $edit-> text;
	$cap = uc $cap unless $self-> caseSensitive;
	my @matchArray;
	my @texts;
	my $maxMatch = 0;
	my $matchId = -1;
	# filling list
	for ( $i = 0; $i < $list-> count; $i++) {
		my $t = $list-> get_item_text($i);
		$t = uc $t unless $self-> caseSensitive;
		push ( @texts, $t);
	}
	# trying to find exact match
	for ( $i = 0; $i < scalar @texts; $i++) {
		if ( $texts[$i] eq $cap) {
			$matchId = $i;
			last;
		}
	}
	if (
		( $style == cs::DropDown) &&
		( $matchId < 0) &&
		defined $edit-> {keyDown}
	) {
		my ($t,$txt);
		for ( $i = 0; $i < scalar @texts; $i++) {
			$txt = $t = $list-> get_item_text($i);
			$t = uc $t unless $self-> caseSensitive;
			last if $t =~ /^\Q$cap\E/;
		}
		# netscape 4 combo behavior
		if ( $i < scalar @texts) {
			$edit-> {interaction} = 1;
			$edit-> text( $txt);
			$edit-> {interaction} = undef;
			$t =~ /^\Q$cap\E/;
			$edit-> selection( length $cap, length $t);
			$list-> focusedItem( $i);
			return;
		}
	}
	# or unexact match
	if ( $matchId < 0) {
		for ( $i = 0; $i < scalar @texts; $i++) {
			my $l = 0;
			$l++ while
				$l < length($texts[$i]) && $l < length($cap)
				&& substr( $texts[$i], $l, 1) eq substr( $cap, $l, 1);
			if ( $l >= $maxMatch) {
				@matchArray = () if $l > $maxMatch;
				$maxMatch = $l;
				push @matchArray, $i;
			}
		}
		$matchId = $matchArray[0] if $matchId < 0;
	}
	$matchId = 0 unless defined $matchId;
	$list-> focusedItem( $matchId);

	if ( $style == cs::DropDownList) {
		$edit-> {interaction} = 1;
		$edit-> text( $list-> get_item_text( $matchId) // '');
		$edit-> {interaction} = undef;
	}
	$list-> {interaction} = undef;
}

sub InputLine_MouseWheel
{
	my ( $self, $edit, $mod, $x, $y, $z) = @_;

	my $f = $self-> {list}-> focusedItem;
	$f += (($z > 0) ? -1 : 1);
	return if $f < 0;
	$self-> {list}-> focusedItem($f);
	$self-> notify(q(Change));
	$edit-> clear_event;
}

sub set_style
{
	my ( $self, $style) = @_;
	return if $self-> {style} == $style;
	my $decr = (( $self-> {style} == cs::Simple) || ( $style == cs::Simple)) ? 1 : 0;
	$self-> {style} = $style;
	if ( $style == cs::Simple) {
		$self-> set(
			height=> $self-> height + $self-> listHeight,
			bottom=> $self-> bottom - $self-> listHeight,
		);
		$self-> {list}-> set(
			visible    => 1,
			origin     => [ 0, 0],
			width      => $self-> width,
			height     => $self-> height - $self-> editHeight ,
			clipOwner  => 1,
			selectable => 1,
		);
	} elsif ( $decr) {
		$self-> set(
			height   => $self-> height - $self-> listHeight,
			bottom   => $self-> bottom + $self-> listHeight,
		);
		$self-> { list}-> set(
			visible    => 0,
			height     => $self-> {listHeight},
			clipOwner  => 0,
			selectable   => $capture_mode ? 0 : 1,
		);
		$self-> listVisible( 0);
	}
	$self-> {edit}-> set(
		bottom => $self-> height - $self-> editHeight ,
		width  => $self-> { edit}-> width + $::application-> uiScaling * DefButtonX * $decr *
			(( $style == cs::Simple) ? 1 : -1),
		height => $self-> editHeight ,
		dndAware => (( $style == cs::DropDown) ? 'Text' : 0 ),
	);
	$self-> {button}-> set(
		bottom => $self-> height - $self-> editHeight ,
		height => $self-> editHeight ,
		visible=> $style != cs::Simple,
	);
	if ( $style == cs::DropDownList) {
		$self-> {edit}-> insertMode(1);
		$self-> {edit}-> text( $self-> {edit}-> text);
	}
}

sub set_list_visible
{
	my ( $self, $nlv) = @_;
	return if ( $self-> {list}-> visible == $nlv) ||
				( $self-> {style} == cs::Simple) ||
				( !$self-> visible && $nlv);
	my ( $list, $edit) = ( $self-> {list}, $self-> {edit});
	if ( $nlv) {
		my @gp = $edit-> client_to_screen( 0, -$list-> height);
		$gp[1] += $edit-> height + $list-> height if $gp[1] < 0;
		$gp[0] = $::application->width - $list->width if $gp[0] + $list->width > $::application->width;
		$list-> origin( @gp);
	}
	$list-> bring_to_front if $nlv;
	$list-> visible( $nlv);
	$self-> {button}-> repaint;
	if ( $capture_mode) {
		$list-> capture( $nlv ? 1 : 0);
		$edit-> focus;
	} else {
		$nlv ? $list-> focus : $edit-> focus;
	}
}

sub set_edit_height
{
	my ( $self, $edit, $list, $btn, $h, $new) =
		($_[0], $_[0]-> {edit}, $_[0]-> {list}, $_[0]-> {button}, $_[0]-> height, $_[1]);
	if ( $self-> style != cs::Simple) {
		$self-> height( $new);
		$edit-> set(
			bottom => 0,
			height => $new
		);
		$btn-> set(
			bottom => 0,
			height => $new
		);
		$list-> height( $self-> {listHeight});
	} else {
		$edit-> set(
			bottom => $h - $new,
			height => $new
		);
		$btn-> set(
			bottom => $h - $new,
			height => $new
		);
		$list-> height( $h - $new);
	}
	$self-> {editHeight} = $new;
}

sub set_list_height
{
	my ( $self, $hei) = @_;
	if ( $self-> style == cs::Simple) {
		$self-> height( $self-> height + $hei - $self-> {listHeight});
	} else {
		$self-> {list}-> height($hei);
	}
	$self-> {listHeight} = $hei;
}

sub get_style       { return $_[0]-> {style}}
sub get_list_visible{ return $_[0]-> {list} ? $_[0]-> {list}-> visible : 0}
sub get_edit_height { return $_[0]-> {edit} ? $_[0]-> {edit}-> height : 0}
sub get_list_height { return  $_[0]-> {list} ? $_[0]-> {list}-> height : 0}

sub caseSensitive{($#_)?$_[0]-> {caseSensitive}=$_[1]:return $_[0]-> {caseSensitive};}
sub listVisible  {($#_)?$_[0]-> set_list_visible($_[1]):return $_[0]-> get_list_visible;}
sub style        {($#_)?$_[0]-> set_style       ($_[1]):return $_[0]-> get_style;       }
sub editHeight   {($#_)?$_[0]-> set_edit_height($_[1]):return $_[0]-> get_edit_height;}
sub listHeight   {($#_)?$_[0]-> set_list_height ($_[1]):return $_[0]-> get_list_height;}
sub literal      {($#_)?$_[0]-> {literal} =      $_[1] :return $_[0]-> {literal}       }

1;

=pod

=head1 NAME

Prima::ComboBox - standard combo box widget

=head1 SYNOPSIS

	use Prima qw(Application ComboBox);

	my $combo = Prima::ComboBox-> new( style => cs::DropDown, items => [ 1 .. 10 ]);
	$combo-> style( cs::DropDownList );
	print $combo-> text;

	run Prima;

=for podview <img src="combo.gif" cut=1>

=for html <p><img src="https://raw.githubusercontent.com/dk/Prima/master/pod/Prima/combo.gif">

=head1 DESCRIPTION

Provides a combo box widget which consists of an input line, list box of possible
selections and eventual drop-down button. The combo box can be either in form
with a drop-down selection list, that is shown by the command of the user,
or in form when the selection list is always visible.

The combo box is a grouping widget, and contains neither painting nor user-input
code. All such functionality is delegated into the children widgets: input line, list
box and button. C<Prima::ComboBox> exports a fixed list of methods and properties from
namespaces of L<Prima::InputLine> and L<Prima::ListBox>. Since, however, it is
possible to tweak the C<Prima::ComboBox> ( using its L<editClass> and L<listClass>
create-only properties ) so the input line and list box would be other classes,
it is not necessarily that all default functionality would work.
The list of exported names is stored in package variables %listProps, %editProps
and %listDynas. These also described in L<Exported names> section.

The module defines C<cs::> package for the constants used by L<style> property.

=head1 API

=head2 Properties

=over

=item autoHeight BOOLEAN

If 1, adjusts the height of the widget automatically when its font changes.
Only when style is not C<cs::Simple>.

Default value: 1

=item buttonClass STRING

Assigns a drop-down button class.

Create-only property.

Default value: C<Prima::Widget>

=item buttonDelegations ARRAY

Assigns a drop-down button list of delegated notifications.

Create-only property.

=item buttonProfile HASH

Assigns hash of properties, passed to the drop-down button during the creation.

Create-only property.

=item caseSensitive BOOLEAN

Selects whether the user input is case-sensitive or not, when a value
is picked from the selection list.

Default value: 0

=item editClass STRING

Assigns an input line class.

Create-only property.

Default value: C<Prima::InputLine>

=item editProfile HASH

Assigns hash of properties, passed to the input line during the creation.

Create-only property.

=item editDelegations ARRAY

Assigns an input line list of delegated notifications.

Create-only property.

=item editHeight INTEGER

Selects height of an input line.

=item items ARRAY

Mapped onto the list widget's C<items> property. See L<Prima::Lists> for details.

=item listClass STRING

Assigns a listbox class.

Create-only property.

Default value: C<Prima::ListBox>

=item listHeight INTEGER

Selects height of the listbox widget.

Default value: 100

=item listVisible BOOLEAN

Sets whether the listbox is visible or not. Not writable
when L<style> is C<cs::Simple>.

=item listProfile HASH

Assigns hash of properties, passed to the listbox during the creation.

Create-only property.

=item listDelegations ARRAY

Assigns a selection listbox list of delegated notifications.

Create-only property.

=item literal BOOLEAN

Selects whether the combo box user input routine assume that
the listbox contains literal strings, that can be fetched via
C<get_item_text> ( see L<Prima::Lists> ). As an example when
this property is set to 0 is C<Prima::ColorComboBox> from L<Prima::ComboBox> package.

Default value: 1

=item style INTEGER

Selected one of three styles:

=over

=item cs::Simple

The listbox is always visible, and the drop-down button is not.

=item cs::DropDown

The listbox is not visible, but the drop-down button is. When the
use presses the drop-down button, the listbox is shown; when the list-box
is defocused, it gets hidden.

=item cs::DropDownList

Same as C<cs::DropDown>, but the user is restricted in the selection:
the input line can only accept user input that is contained in listbox.
If L<literal> set to 1, the auto completion feature is provided.

=back

=item text STRING

Mapped onto the edit widget's C<text> property.

=back

=head2 Events

=over

=item Change

Triggered with ComboBox value is changed.

=item List events

ComboBox forwards C<SelectItem> and C<DrawItem> events from the list box, and
these are executed in the List's context (therefore $self there is not
ComboBox, but the ComboBox->List). If you use C<SelectItem> you probably need
C<Change> instead.

See more in L<Prima::Lists>.

=back

=head2 Exported names

=over

=item %editProps

	alignment      autoScroll  text         text
	charOffset     maxLen      insertMode   firstChar
	selection      selStart    selEnd       writeOnly
	copy           cut         delete       paste
	wordDelimiters readOnly    passwordChar focus
	select_all

=item %listProps

		       focusedItem    hScroll
	integralHeight items          itemHeight
	topItem        vScroll        gridColor
	multiColumn    offset

=item %listDynas

	onDrawItem
	onSelectItem

=back

=head1 AUTHOR

Dmitry Karasik, E<lt>dmitry@karasik.eu.orgE<gt>.

=head1 SEE ALSO

L<Prima>, L<Prima::InputLine>, L<Prima::Lists>, L<Prima::Dialog::ColorDialog>, L<Prima::Dialog::FileDialog>,
F<examples/listbox.pl>.

=cut