#!/usr/bin/perl -w
# -*- perl -*-

# This file is a Tcl script to test entry widgets in Tk.  It is
# organized in the standard fashion for Tcl tests.
#
# Copyright (c) 1994 The Regents of the University of California.
# Copyright (c) 1994-1997 Sun Microsystems, Inc.
# Copyright (c) 1998-1999 by Scriptics Corporation.
# All rights reserved.
#
# Translated to Perl/Tk by Slaven Rezic

use strict;

use Tk;
use Tk::Trace;
use Tk::Config ();
my $Xft = $Tk::Config::xlib =~ /-lXft\b/;


BEGIN {
    if (!eval q{
	use Test::More;
	1;
    }) {
	print "# tests only work with installed Test::More module\n";
	print "1..1\n";
	print "ok 1\n";
	exit;
    }
}

BEGIN {
        # these fail (sometimes) under 'make test'
        my @fragile = qw(160 161 167 191 193 195);
        @fragile = () ; # unless $ENV{PERL_DL_NONLAZY};
        plan tests => 350,
        todo => \@fragile
      }

use Getopt::Long;

my $do_wm_test   = (defined($ENV{WINDOWMANAGER}) &&
		    $ENV{WINDOWMANAGER} eq '/usr/X11R6/bin/kde');
my $do_font_test = 1;

GetOptions("wmtest!" => \$do_wm_test)
    or die "usage: $0 [-wmtest]";

my $skip_wm_test = "window manager dependent tests";
my $skip_font_test = "font-related tests";
my $skip_wm_font_test = "window manager and/or font-related tests";

my $mw = Tk::MainWindow->new();
$mw->geometry('+10+10');

my $e0 = $mw->Entry;
ok(Tk::Exists($e0), "Entry widget exists");
isa_ok($e0, "Tk::Entry");

my @scrollInfo;
sub scroll {
    (@scrollInfo) = @_;
}

# Create additional widget that's used to hold the selection at times.

my $sel = $mw->Entry;
$sel->insert("end", "This is some sample text");

# Font names

my $big   = $Xft ? '{Adobe Helvetica} -24' : "-adobe-helvetica-medium-r-normal--24-240-75-75-p-*-iso8859-1";
## Always use the X11 font, even with Xft, otherwise the measurements would
## be wrong.
#my $fixed = $Xft ? '{Adobe Courier} -12' : "-adobe-courier-medium-r-normal--12-120-75-75-m-*-iso8859-1";
my $fixed = "-adobe-courier-medium-r-normal--12-120-75-75-m-*-iso8859-1";

# Create entries in the option database to be sure that geometry options
# like border width have predictable values.

$mw->option("add", "*Entry.borderWidth", 2);
$mw->option("add", "*Entry.highlightThickness", 2);
## Again, prefer the X11 font.
#$mw->option("add", "*Entry.font", $Xft ? '{Adobe Helvetica} -12' : "Helvetica -12");
$mw->option("add", "*Entry.font", "Helvetica -12");

my $e = $mw->Entry(qw(-bd 2 -relief sunken))->pack;
$mw->update;

if (!$Xft) { # XXX Is this condition necessary?
    my %fa = $mw->fontActual($e->cget(-font));
    my %expected = (
		    "-weight" => "normal",
		    "-underline" => 0,
		    "-family" => "helvetica",
		    "-slant" => "roman",
		    "-size" => -12,
		    "-overstrike" => 0,
		   );
    while(my($k,$v) = each %expected) {
	if ($v ne $fa{$k}) {
	    $do_font_test = 0;
	    last;
	}
    }
}

my $i;

use constant SKIP_CGET    => 5;
use constant SKIP_CONF    => 6;
use constant SKIP_ERROR   => 7;
use constant SKIP_RESTORE => 8;

my @tests = (
    [qw(-background), '#ff0000', '#ff0000', 'non-existent',
	    'unknown color name "non-existent"'],
    [qw(-bd 4 4 badValue), 'bad screen distance "badValue"'],
    [qw(-bg), '#ff0000', '#ff0000', 'non-existent', 'unknown color name "non-existent"'],
    [qw(-borderwidth 1.3 1 badValue), 'bad screen distance "badValue"'],
    [qw(-cursor arrow arrow badValue), 'bad cursor spec "badValue"'],
    [qw(-disabledbackground green green non-existent),
        q{unknown color name "non-existent"}],
    [qw(-disabledforeground blue blue non-existent),
        q{unknown color name "non-existent"}],
    [qw(-exportselection yes 1 xyzzy), 'expected boolean value but got "xyzzy"', 0,0,1],
    [qw(-fg), '#110022', '#110022', 'bogus', 'unknown color name "bogus"'],
    [qw(-font -Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*
	-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*), undef,
        'font "" doesn\'t exist',
        1,0,1],
    [qw(-foreground), '#110022', '#110022', 'bogus', 'unknown color name "bogus"'],
    [qw(-highlightbackground), '#123456', '#123456', 'ugly', 'unknown color name "ugly"'],
    [qw(-highlightcolor), '#123456', '#123456', 'bogus', 'unknown color name "bogus"'],
    [qw(-highlightthickness 6 6 bogus), 'bad screen distance "bogus"'],
    [qw(-highlightthickness -2 0), undef, undef],
    [qw(-insertbackground), '#110022', '#110022', 'bogus', 'unknown color name "bogus"'],
    [qw(-insertborderwidth 1.3 1 2.6x), 'bad screen distance "2.6x"'],
    [qw(-insertofftime 100 100 3.2), 'expected integer but got "3.2"', 0,0,1],
    [qw(-insertontime 100 100 3.2), 'expected integer but got "3.2"', 0,0,1],
    [qw(-invalidcommand), \&Tk::NoOp, \&Tk::NoOp, undef, undef],
    [qw(-invcmd), \&Tk::NoOp, \&Tk::NoOp, undef, undef],
    [qw(-justify right right bogus), 'bad justification "bogus": must be left, right, or center'],
    [qw(-readonlybackground green green non-existent),
        q{unknown color name "non-existent"}],
    [qw(-relief groove groove 1.5), 'bad relief "1.5": must be flat, groove, raised, ridge, solid, or sunken'],
    [qw(-selectbackground), '#110022', '#110022', 'bogus', 'unknown color name "bogus"'],
    [qw(-selectborderwidth 1.3 1 badValue), 'bad screen distance "badValue"'],
    [qw(-selectforeground), '#654321', '#654321', 'bogus', 'unknown color name "bogus"'],
    [qw(-show * *), undef, undef],
    [qw(-state n normal bogus), 'bad state "bogus": must be disabled, normal, or readonly'],
    [qw(-takefocus), "any string", "any string", undef, undef],
    [qw(-textvariable), \$i, \$i, undef, undef],
    [qw(-width 402 402 3p), "'3p' isn't numeric"],
    [qw(-xscrollcommand), 'Some command', 'Some command', undef, undef, 1,1,1,1],
);

foreach my $test (@tests) {
    my $name = $test->[0];
    $e->configure($name, $test->[1]);
    if (!$test->[SKIP_CGET]) {
	my $val = $e->cget($name);
	if (UNIVERSAL::isa($val, "Tk::Callback")) {
	    $val = $val->[0];
	}
	is($val, $test->[2], "cget $name");
    }
    if (!$test->[SKIP_CONF]) {
	is(($e->configure($name))[4], $e->cget($name), "Comparing configure and cget values for $name");
    }

    if (defined $test->[4]) {
	if (!$test->[SKIP_ERROR]) {
	    eval { $e->configure($name, $test->[3]) };
	    like($@, qr/$test->[4]/, "Expected error message for $name");
	}
    }
    if (!$test->[SKIP_RESTORE]) {
	$e->configure($name, ($e->configure($name))[3]);
    }
}

eval { $e->destroy };
$e = $mw->Entry;
ok(Tk::Exists($e));
is($e->class, 'Entry', "Tk class name");
isa_ok($e, 'Tk::Entry');

eval { $e->destroy; undef $e };
eval { $e = $mw->Entry(-gorp => 'foo') };
like($@, qr/unknown option "-gorp"/);
ok(!Tk::Exists($e), "Expected failure while creating entry widget with bad options");
ok(!defined $e);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed)->pack;
$e->update;

my $cx = $mw->fontMeasure($fixed, 'a');
my $cy = $mw->fontMetrics($fixed, '-linespace');
my $ux = $mw->fontMeasure($fixed, "\x{4e4e}");

eval { $e->bbox };
like($@, qr/wrong \# args: should be ".* bbox index"/, "bbox error message");

eval { $e->bbox(qw(a b)) };
like($@, qr/wrong \# args: should be ".* bbox index"/);

eval { $e->bbox(qw(bogus)) };
like($@, qr/bad entry index "bogus"/);

$e->delete(0,"end");
is_deeply([$e->bbox(0)],[5,5,0,$cy], "Expected bbox");

$e->delete(0,"end");
$e->insert(0,"abc");
is_deeply([$e->bbox(3)],[5+2*$cx,5,$cx,$cy]);
is_deeply([$e->bbox("end")],[$e->bbox(3)]);

$e->delete(0,"end");
$e->insert(0,"ab\x{4e4e}");
is_deeply([$e->bbox("end")],[5+2*$cx,5,$ux,$cy], "Bbox check with unicode char (at end)");

$e->delete(0,"end");
$e->insert(0,"ab\x{4e4e}c");
is_deeply([$e->bbox(3)],[5+2*$cx+$ux,5,$cx,$cy], "Bbox check with unicode char (before index)");

$e->delete(0,"end");
is_deeply([5,5,0,$cy],[$e->bbox("end")]);

$e->delete(0,"end");
$e->insert(0,"abcdefghij\x{4e4e}klmnop");
is_deeply([[$e->bbox(0)],
	   [$e->bbox(1)],
	   [$e->bbox(10)],
	   [$e->bbox("end")]],
	  [[5,5,$cx,$cy],
	   [5+$cx,5,$cx,$cy],
	   [5+10*$cx,5,$ux,$cy],
	   [5+$ux+15*$cx,5,$cx,$cy]], "More bbox checks with unicode char");

eval { $e->cget };
like($@, qr/wrong \# args: should be ".* cget option"/, "cget error message");

eval { $e->cget(qw(a b)) };
like($@, qr/wrong \# args: should be ".* cget option"/);

eval { $e->cget(-gorp) };
like($@, qr/unknown option "-gorp"/);

$e->configure(-bd => 4);
is($e->cget(-bd), 4);
is(scalar @{$e->configure}, 36);

eval { $e->configure('-foo') };
like($@, qr/unknown option "-foo"/, "Unknown option error message");

$e->configure(-bd => 4);
$e->configure(-bg => '#ffffff');
is(($e->configure(-bd))[4], 4);

eval { $e->delete };
like($@, qr/wrong \# args: should be ".* delete firstIndex \?lastIndex\?"/, "delete error message");

eval { $e->delete(qw(a b c)) };
like($@, qr/wrong \# args: should be ".* delete firstIndex \?lastIndex\?"/);

eval { $e->delete("foo") };
like($@, qr/bad entry index "foo"/);

eval { $e->delete(0, "bar") };
like($@, qr/bad entry index "bar"/);

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->delete(2, 4);
is($e->get, "014567890", "Expected get after insert");

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->delete(6);
is($e->get, "0123457890");

$e->delete(0, "end");
$e->insert("end", "01234\x{4e4e}67890");
$e->delete(6);
is($e->get, "01234\x{4e4e}7890", "Delete with unicode character before");

$e->delete(0, "end");
$e->insert("end", "012345\x{4e4e}7890");
$e->delete(6);
is($e->get, "0123457890", "Delete unicode character");

$e->delete(0, "end");
$e->insert("end", "0123456\x{4e4e}890");
$e->delete(6);
is($e->get, "012345\x{4e4e}890", "Delete with unicode character after");

$e->delete(0,"end");
$e->insert("end", "01234567890");
$e->delete(6, 5);
is($e->get, "01234567890", "Delete reversed range");

$e->delete(0,"end");
$e->insert("end", "01234567890");
$e->configure(-state => 'disabled');
$e->delete(2, 8);
$e->configure(-state => 'normal');
is($e->get, "01234567890", "Delete while disabled state");

eval { $e->get("foo") };
like($@, qr/wrong \# args: should be ".* get"/, "get error message");

eval { $e->icursor };
like($@, qr/wrong \# args: should be ".* icursor pos"/);

eval { $e->icursor("foo") };
like($@, qr/bad entry index "foo"/);

$e->delete(0,"end");
$e->insert("end", "01234567890");
$e->icursor(4);
is($e->index('insert'), 4, "Index method");

eval { $e->in };
like($@, qr/Can\'t locate(?: file)? auto\/Tk\/Entry\/in\.al/, "Invalid abbreviated method name (index)");

eval { $e->index };
like($@, qr/wrong \# args: should be ".* index string"/, "index error message");

eval { $e->index("foo") };
like($@, qr/bad entry index "foo"/);

is($e->index(0), 0);

$e->delete(0,"end");
$e->insert(0, "abc\x{4e4e}\x{0153}def");
is_deeply([$e->index(3), $e->index(4), $e->index("end")],[3,4,8], "Index with unicode characters");

eval { $e->insert(qw(a)) };
like($@, qr/wrong \# args: should be ".* insert index text"/, "insert error message");

eval { $e->insert(qw(a b c)) };
like($@, qr/wrong \# args: should be ".* insert index text"/);

eval { $e->insert(qw(foo Text)) };
like($@, qr/bad entry index "foo"/);

$e->delete(0,"end");
$e->insert("end", "01234567890");
$e->insert(3, "xxx");
is($e->get, '012xxx34567890', "get after insert method call");

$e->delete(0,"end");
$e->insert("end", "01234567890");
$e->configure(qw(-state disabled));
$e->insert(qw(3 xxx));
$e->configure(qw(-state normal));
is($e->get, "01234567890");

eval { $e->insert(qw(a b c)) };
like($@, qr/wrong \# args: should be ".* insert index text"/);

eval { $e->scan(qw(a)) };
like($@, qr/wrong \# args: should be ".* scan mark\|dragto x"/, "scan error message");

eval { $e->scan(qw(a b c)) };
like($@, qr/wrong \# args: should be ".* scan mark\|dragto x"/);

eval { $e->scan(qw(foobar 20)) };
like($@, qr/bad scan option "foobar": must be mark or dragto/);

eval { $e->scan(qw(mark 20.1)) };
is($@, '', "Correct mark method call");

# This test is non-portable because character sizes vary.

$e->delete(qw(0 end));
$e->update;
$e->insert(end => "This is quite a long string, in fact a ");
$e->insert(end => "very very long string");
$e->scan(qw(mark 30));
$e->scan(qw(dragto 28));
is($e->index('@0'), 2, "Index of a scrolled string");

eval {$e->select };
like($@, qr/Can\'t locate(?: file)? auto\/Tk\/Entry\/select\.al/, "Invalid abbreviated method name (selection)");

eval {$e->selection };
like($@, qr/wrong \# args: should be ".* selection option \?index\?"/, "selection error message");

eval {$e->selection('foo') };
like($@, qr/bad selection option "foo": must be adjust, clear, from, present, range, or to/);

eval { $e->selection("clear", "gorp") };
like($@, qr/wrong \# args: should be ".* selection clear"/);

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selection("from", 1);
$e->selection("to", 4);
$e->update;
$e->selection("clear");
eval { $mw->SelectionGet };
like($@, qr/PRIMARY selection doesn\'t exist or form "(UTF8_)?STRING" not defined/);
is($mw->SelectionOwner, $e, "Expected selection owner");

eval { $e->selection("present", "foo") };
like($@, qr/wrong \# args: should be ".* selection present"/);

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selectionFrom(3);
$e->selectionTo(6);
ok($e->selectionPresent, "Selection is now present");

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selectionFrom(3);
$e->selectionTo(6);
$e->configure(-exportselection => 0);
ok($e->selection('present'), "Selection is also present with no exportselection");

$e->configure(-exportselection => 1);

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selectionFrom(3);
$e->selectionTo(6);
$e->delete(0,"end");
ok(!$e->selectionPresent, "Selection is not present, -exportselection set to true");

eval { $e->selectionAdjust("x") };
like($@, qr/bad entry index "x"/, "selectionAdjust error message");

eval { $e->selection(qw(adjust 2 3)) };
like($@, qr/wrong \# args: should be ".* selection adjust index"/);

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selectionFrom(1);
$e->selection(qw(to 5));
$e->update;
$e->selectionAdjust(4);
is($mw->SelectionGet, "123", "Expected result with selectionGet");

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selectionFrom(1);
$e->selection(qw(to 5));
$e->update;
$e->selectionAdjust(2);
is($mw->SelectionGet, "234");

eval { $e->selectionFrom(qw(2 3)) };
like($@, qr/wrong \# args: should be ".* selection from index"/, "selectionFrom error message");

eval { $e->selection(qw(range 2)) };
like($@, qr/wrong \# args: should be ".* selection range start end"/, "selectionRange error message");

eval { $e->selection(qw(range 2 3 4)) };
like($@, qr/wrong \# args: should be ".* selection range start end"/);

$e->delete(0, "end");
$e->insert("end", "01234567890");
$e->selectionFrom(1);
$e->selection(qw(to 5));
$e->selection(qw(range 4 4 ));
eval { $e->index("sel.first") };
like($@, qr/selection isn\'t in widget/);

$e->delete(0, "end");
$e->insert("end", "0123456789");
$e->selectionFrom(3);
$e->selection(qw(to 7));
$e->selection(qw(range 2 9));
is($e->index("sel.first"), 2, "Index of selection");
is($e->index("sel.last"), 9);
is($e->index("anchor"), 3);

$e->delete(qw(0 end));
$e->insert(end => "This is quite a long text string, so long that it ");
$e->insert(end => "runs off the end of the window quite a bit.");

eval { $e->selectionTo(2,3) };
like($@, qr/wrong \# args: should be ".* selection to index"/);

$e->xview(5);
is_deeply([map { substr($_, 0, 8) } $e->xview],["0.053763","0.268817"], "Expected xview result");

eval { $e->xview(qw(gorp)) };
like($@, qr/bad entry index "gorp"/, "xview error message");

$e->xview(0);
$e->icursor(10);
$e->xview('insert');
is_deeply([map { substr($_, 0, 7) } $e->xview],["0.10752","0.32258"]);

eval { $e->xviewMoveto(qw(foo bar)) };
like($@, qr/wrong \# args: should be ".* xview moveto fraction"/);

eval { $e->xview(qw(moveto foo)) };
like($@, qr/\'foo\' isn\'t numeric/);

$e->xviewMoveto(0.5);
is_deeply([map { substr($_, 0, 7) } $e->xview],["0.50537","0.72043"]);

eval { $e->xviewScroll(24) };
like($@, qr/wrong \# args: should be ".* xview scroll number units\|pages"/, "xviewScroll error message");

eval { $e->xviewScroll(qw(gorp units)) };
like($@, qr/\'gorp\' isn\'t numeric/);

$e->xviewMoveto(0);
$e->xview(qw(scroll 1 pages));
is_deeply([map { substr($_, 0, 7) } $e->xview],["0.19354","0.40860"]);

$e->xview(qw(moveto .9));
$e->update;
$e->xview(qw(scroll -2 p));
is_deeply([map { substr($_, 0, 7) } $e->xview],["0.39784","0.61290"]);

$e->xview(30);
$e->update;
$e->xview(qw(scroll 2 units));
is($e->index('@0'), 32);

$e->xview(30);
$e->update;
$e->xview(qw(scroll -1 units));
is($e->index('@0'), 29);

eval { $e->xviewScroll(23,"foobars") };
like($@, qr/bad argument "foobars": must be units or pages/);

eval { $e->xview(qw(eat 23 hamburgers)) };
like($@, qr/unknown option "eat": must be moveto or scroll/);

$e->xview(0);
$e->update;
$e->xview(-4);
is($e->index('@0'), 0);

$e->xview(300);
is($e->index('@0'), 73);

{
    $e->insert(10, "\x{4e4e}");

    my @x;
    $e->xviewMoveto(0.1);
    push @x, ($e->xview)[0];
    $e->xviewMoveto(0.11);
    push @x, ($e->xview)[0];
    $e->xviewMoveto(0.12);
    push @x, ($e->xview)[0];

    is_deeply([map { substr($_, 0, 7) } @x],["0.09574","0.10638","0.11702"], "xviewMoveto with unicode character");
}

eval { $e->gorp };
like($@, qr/Can\'t locate(?: file)? auto\/Tk\/Entry\/gorp\.al/, "Invalid method");

# The test below doesn't actually check anything directly, but if run
# with Purify or some other memory-allocation-checking program it will
# ensure that resources get properly freed.

eval { $e->destroy };
my $x;
$e = $mw->Entry(-textvariable => \$x, -show => '*')->pack;
$e->insert('end', "Sample text");
$e->update;
$e->destroy;

my $f = $mw->Frame(qw(-width 200 -height 50 -relief raised -bd 2))
    ->pack(-side => "right");

#eval { $e->destroy };
$x = 12345;
$e = $mw->Entry(-textvariable => \$x);
is($e->get, "12345", "-textvariable check");

eval { $e->destroy };
$x = "12345";
$e = $mw->Entry(-textvariable => \$x);
my $y = "abcde";
$e->configure(-textvariable => \$y);
$x = 54321;
is($e->get, "abcde");

eval { $e->destroy };
undef $x;
$e = $mw->Entry;
$e->configure(-textvariable => \$x);
$e->insert(0, "Some text");
is($x, "Some text");

eval { $e->destroy };
undef $x;
$e = $mw->Entry;
$e->insert(0, "Some text");
$e->configure(-textvariable => \$x);
#is($x, "Some text"); # XXX does not work with Perl/Tk!

sub override {
    $x = 12345;
}

## XXX traceVariable does not work?
eval { $e->destroy };
undef $x;
$mw->traceVariable(\$x, 'w' => \&override);
$e = $mw->Entry;
$e->configure(-textvariable => \$x);
$e->insert('0', "Some text");
my @result = ($x,$e->get);
undef $x;
is($result[0], "12345", "-textvariable with traceVariable");
is($result[1], "12345");

eval { $e->destroy };
$e = $mw->Entry(-exportselection => 0)->pack;
$e->insert(qw(end 0123456789));
$sel->selectionFrom(0);
$sel->selectionTo(10);
is($mw->SelectionGet, "This is so");
$e->selectionFrom(1);
$e->selectionTo(5);
is($mw->SelectionGet, "This is so");
$e->configure(-exportselection => 1);
is($mw->SelectionGet, "1234");

eval { $e->destroy };
$e = $mw->Entry->pack;
$e->insert(qw(end 0123456789));
$e->selectionFrom(1);
$e->selectionTo(5);
$e->configure(-exportselection => 0);
eval { $mw->SelectionGet };
like($@, qr/PRIMARY selection doesn\'t exist or form "(UTF8_)?STRING" not defined/);
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 5);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, qw(-width 4 -xscrollcommand), \&scroll)->pack;
$e->insert(qw(end 01234567890));
$e->update;
$e->configure(qw(-width 5));
is_deeply([map { substr($_, 0, 8) } @scrollInfo], [0,0.363636]);

eval { $e->destroy };

$e = $mw->Entry(-width => 0)->pack;
$e->insert(end => "0123");
$e->update;
$e->configure(-font => $big);
$e->update;
SKIP: {
    skip($skip_wm_test, 1)
	if !$do_wm_test;
    like($e->geometry, qr/62x3\d\+0\+0/, "Geometry check");
}

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, -bd => 2, -relief => "raised")->pack;
$e->insert(end => "0123");
$e->update;
is($e->index('@10'), 0, "index with raised relief");
is($e->index('@11'), 0);
is($e->index('@12'), 1);
is($e->index('@13'), 1);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, -bd => 2, -relief => "flat")->pack;
$e->insert(end => "0123");
$e->update;
is($e->index('@10'), 0, "index with flat relief");
is($e->index('@11'), 0);
is($e->index('@12'), 1);
is($e->index('@13'), 1);

# If "0" in selected font had 0 width, caused divide-by-zero error.

eval { $e->destroy };
$e = $mw->Entry(-font => '{open look glyph}')->pack;
$e->scan('dragto', 30);
$e->update;

# No tests for DisplayEntry.

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, -bd => 2, -relief => "raised", -width => 20, -highlightthickness => 3)->pack;
$e->insert("end", "012\t45");
$e->update;
is($e->index('@61'), 3, "index with highlightthickness");
is($e->index('@62'), 4);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, qw(-bd 2 -relief raised -width 20 -justify center -highlightthickness 3))->pack;
$e->insert("end", "012\t45");
$e->update;
is($e->index('@96'), 3, "index with center justify");
is($e->index('@97'), 4);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, qw(-bd 2 -relief raised -width 20 -justify right -highlightthickness 3))->pack;
$e->insert("end", "012\t45");
$e->update;
is($e->index('@131'), 3, "index with right justify");
is($e->index('@132'), 4);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, qw(-bd 2 -relief raised -width 5))->pack;
$e->insert(qw(end 01234567890));
$e->update;
$e->xview(6);
is($e->index('@0'), 6);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, qw(-bd 2 -relief raised -width 5))->pack;
$e->insert(qw(end 01234567890));
$e->update;
$e->xview(7);
is($e->index('@0'), 6);

eval { $e->destroy };
$e = $mw->Entry(-font => $fixed, qw(-bd 2 -relief raised -width 10))->pack;
$e->insert(qw(end), "01234\t67890");
$e->update;
$e->xview(3);
is($e->index('@39'), 5, "index with tabulator in entry");
is($e->index('@40'), 6);

SKIP: {
    skip($skip_wm_test, 10)
	if !$do_wm_test;

    eval { $e->destroy };
    $e = $mw->Entry(-font => $big, qw(-bd 3 -relief raised -width 5))->pack;
    $e->insert(qw(end), "01234567");
    $e->update;
    is($e->reqwidth, 77, "Expected reqwidth");
    is($e->reqheight, 39, "Expected reqheight");

    eval { $e->destroy };
    $e = $mw->Entry(-font => $big, qw(-bd 3 -relief raised -width 0))->pack;
    $e->insert(qw(end), "01234567");
    $e->update;
    is($e->reqwidth, 116);
    is($e->reqheight, 39);

    eval { $e->destroy };
    $e = $mw->Entry(-font => $big, qw(-bd 3 -relief raised -width 0 -highlightthickness 2))->pack;
    $e->update;
    is($e->reqwidth, 25);
    is($e->reqheight, 39);

    eval { $e->destroy };
    $e = $mw->Entry(qw(-bd 1 -relief raised -width 0 -show .))->pack;
    $e->insert(0, "12345");
    $e->update;
    is($e->reqwidth, 23);
    $e->configure(-show => 'X');
    is($e->reqwidth, 53);
    #$e->configure(-show => '');
    #is($e->reqwidth, 43);

    eval { $e->destroy };
    $e = $mw->Entry(qw(-bd 1 -relief raised -width 0 -show .),
		    -font => 'helvetica 12')->pack;
    $e->insert(0, "12345");
    $e->update;
    is($e->reqwidth, 8+5*$mw->fontMeasure("helvetica 12", "."));
    $e->configure(-show => 'X');
    is($e->reqwidth, 8+5*$mw->fontMeasure("helvetica 12", "X"));
    #$e->configure(-show => '');
    #is($e->reqwidth, 8+$mw->fontMeasure("helvetica 12", "12345"));
}

eval { $e->destroy };
my $contents;
$e = $mw->Entry(qw(-width 10 -font), $fixed, -textvariable => \$contents,
		-xscrollcommand => \&scroll)->pack;
$e->focus;
$e->delete(0, "end");
$e->insert(0, "abcde");
$e->insert(2, "XXX");
$e->update;
is($e->get, "abXXXcde");
is($contents, "abXXXcde");
is(join(" ", @scrollInfo), "0 1", "Result collected in -xscrollcommand callback");

$e->delete(0, "end");
$e->insert(0, "abcde");
$e->insert(500, "XXX");
$e->update;
is($e->get, "abcdeXXX");
is($contents, "abcdeXXX");
is(join(" ", @scrollInfo), "0 1");

$e->delete(0, "end");
$e->insert(0, "0123456789");
$e->selectionFrom(2);
$e->selectionTo(6);
$e->insert(2, "XXX");
is($e->index("sel.first"), 5);
is($e->index("sel.last"), 9);
$e->selectionTo(8);
is($e->index("sel.first"), 5);
is($e->index("sel.last"), 8);

$e->delete(0, "end");
$e->insert(0, "0123456789");
$e->selectionFrom(2);
$e->selectionTo(6);
$e->insert(3, "XXX");
is($e->index("sel.first"), 2);
is($e->index("sel.last"), 9);
$e->selectionTo(8);
is($e->index("sel.first"), 2);
is($e->index("sel.last"), 8);

$e->delete(0, "end");
$e->insert(0, "0123456789");
$e->selectionFrom(2);
$e->selectionTo(6);
$e->insert(5, "XXX");
is($e->index("sel.first"), 2);
is($e->index("sel.last"), 9);
$e->selectionTo(8);
is($e->index("sel.first"), 2);
is($e->index("sel.last"), 8);

$e->delete(0, "end");
$e->insert(0, "0123456789");
$e->selectionFrom(2);
$e->selectionTo(6);
$e->insert(6, "XXX");
is($e->index("sel.first"), 2);
is($e->index("sel.last"), 6);
$e->selectionTo(5);
is($e->index("sel.first"), 2);
is($e->index("sel.last"), 5);

$e->delete(0, "end");
$e->insert(0, "0123456789");
$e->icursor(4);
$e->insert(4, "XXX");
is($e->index("insert"), 7);

$e->delete(0, "end");
$e->insert(0, "0123456789");
$e->icursor(4);
$e->insert(5, "XXX");
is($e->index("insert"), 4);

$e->delete(0, "end");
$e->insert(0, "This is a very long string");
$e->update;
$e->xview(4);
$e->insert(qw(3 XXX));
is($e->index('@0'), 7);

$e->delete(0, "end");
$e->insert(0, "This is a very long string");
$e->update;
$e->xview(4);
$e->insert(qw(4 XXX));
is($e->index('@0'), 4);

$e->delete(0, "end");
$e->insert(0, "xyzzy");
$e->update;
$e->insert(2, "00");
## XXX is($e->reqwidth, 59);

$e->delete(qw(0 end));
$e->insert(qw(0 abcde));
$e->delete(qw(2 4));
$e->update;
is($e->get, "abe");
is($contents, "abe");
is($scrollInfo[0], 0);
is($scrollInfo[1], 1);

$e->delete(qw(0 end));
$e->insert(qw(0 abcde));
$e->delete(qw(-2 2));
$e->update;
is($e->get, "cde");
is($contents, "cde");
is($scrollInfo[0], 0);
is($scrollInfo[1], 1);

$e->delete(qw(0 end));
$e->insert(qw(0 abcde));
$e->delete(qw(3 1000));
$e->update;
is($e->get, "abc");
is($contents, "abc");
is($scrollInfo[0], 0);
is($scrollInfo[1], 1);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 3));
$e->selection(qw(to 8));
$e->delete(qw(1 3));
$e->update;
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 6);
$e->selectionTo(5);
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 5);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 3));
$e->selection(qw(to 8));
$e->delete(qw(1 4));
$e->update;
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 5);
$e->selectionTo(4);
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 4);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 3));
$e->selection(qw(to 8));
$e->delete(qw(1 7));
$e->update;
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 2);
$e->selectionTo(5);
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 5);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 3));
$e->selection(qw(to 8));
$e->delete(qw(1 8));
eval { $e->index("sel.first") };
like($@, qr/selection isn\'t in widget/, "Expected error, no selection");

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 3));
$e->selection(qw(to 8));
$e->delete(qw(3 7));
$e->update;
is($e->index("sel.first"), 3);
is($e->index("sel.last"), 4);
$e->selectionTo(8);
is($e->index("sel.first"), 3);
is($e->index("sel.last"), 8);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 3));
$e->selection(qw(to 8));
$e->delete(qw(3 8));
eval { $e->index("sel.first") };
like($@, qr/selection isn\'t in widget/);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 8));
$e->selection(qw(to 3));
$e->delete(qw(5 8));
$e->update;
is($e->index("sel.first"), 3);
is($e->index("sel.last"), 5);
$e->selectionTo(8);
is($e->index("sel.first"), 5);
is($e->index("sel.last"), 8);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->selection(qw(from 8));
$e->selection(qw(to 3));
$e->delete(qw(8 10));
$e->update;
is($e->index("sel.first"), 3);
is($e->index("sel.last"), 8);
$e->selectionTo(4);
is($e->index("sel.first"), 4);
is($e->index("sel.last"), 8);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->icursor(4);
$e->delete(qw(1 4));
is($e->index("insert"), 1);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->icursor(4);
$e->delete(qw(1 5));
is($e->index("insert"), 1);

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcde));
$e->icursor(4);
$e->delete(qw(4 6));
is($e->index("insert"), 4);

$e->delete(qw(0 end));
$e->insert(qw(0), "This is a very long string");
$e->xview(4);
$e->delete(qw(1 4));
is($e->index('@0'), 1);

$e->delete(qw(0 end));
$e->insert(qw(0), "This is a very long string");
$e->xview(4);
$e->delete(qw(1 5));
is($e->index("\@0"), 1);

$e->delete(qw(0 end));
$e->insert(qw(0), "This is a very long string");
$e->xview(4);
$e->delete(qw(4 6));
is($e->index("\@0"), 4);

$e->configure(qw(-width 0));

$e->delete(qw(0 end));
$e->insert(0, "xyzzy");
$e->update;
$e->delete(qw(2 4));
is($e->reqwidth, 31);

eval { $e->destroy };

sub _override2 {
    $x = "12345";
}
undef $x;
$mw->traceVariable(\$x, 'w', \&_override2);
$e = $mw->Entry(-textvariable => \$x);
$e->insert(0, "foo");
is($x, 12345);
is($e->get, 12345);
undef $x;

Tk::catch {$e->destroy};
$e = $mw->Entry->pack;
$e->configure(-width => 0);

$x = "abcde";
$y = "ab";
$e->configure(-textvariable => \$x);
$e->update;
$e->configure(-textvariable => \$y);
$e->update;
is($e->get, "ab");
# On Unix/X11 and Windows it's 24, on cygwin/X11 with Xvfb running it's 25,
# on Mac OS X with XFT=1 and a remote Xserver it's 23:
cmp_ok($e->reqwidth, ">=", 23);
cmp_ok($e->reqwidth, "<=", 25);

$mw->traceVdelete(\$x); # XXX why?

Tk::catch {$e->destroy};
$e = $mw->Entry(-textvariable => \$x);
$e->insert(0, "abcdefghjklmnopqrstu");
$e->selection(qw(range 4 10));
$x = "a";
eval { $e->index("sel.first") };
like($@, qr/selection isn\'t in widget/);

Tk::catch {$e->destroy};
$e = $mw->Entry(-textvariable => \$x);
$e->insert(0, "abcdefghjklmnopqrstu");
$e->selection(qw(range 4 10));
$x = "abcdefg";
is($e->index("sel.first"), 4);
is($e->index("sel.last"), 7);

Tk::catch {$e->destroy};
$e = $mw->Entry(-textvariable => \$x);
$e->insert(0, "abcdefghjklmnopqrstu");
$e->selection(qw(range 4 10));
$x = "abcdefghijklmn";
is($e->index("sel.first"), 4);
is($e->index("sel.last"), 10);

Tk::catch {$e->destroy};
$e = $mw->Entry(-textvariable => \$x)->pack;
$e->insert(0, "abcdefghjklmnopqrstu");
$e->xview(10);
$e->update;
$x = "abcdefg";
$e->update;
is($e->index('@0'), 0);

Tk::catch {$e->destroy};
$e = $mw->Entry(-font => $fixed, -width => 10, -textvariable => \$x)->pack;
$e->insert(0, "abcdefghjklmnopqrstu");
$e->xview(10);
$e->update;
$x = "1234567890123456789012";
$e->update;
is($e->index('@0'), 10);

Tk::catch {$e->destroy};
$e = $mw->Entry(-font => $fixed, -width => 10, -textvariable => \$x)->pack;
$e->insert(0, "abcdefghjklmnopqrstu");
$e->icursor(5);
$x = "123";
is($e->index("insert"), 3);

Tk::catch {$e->destroy};
$e = $mw->Entry(-font => $fixed, -width => 10, -textvariable => \$x)->pack;
$e->insert(0, "abcdefghjklmnopqrstuvwxyz");
$e->icursor(5);
$x = "123456";
is($e->index("insert"), 5);

Tk::catch {$e->destroy};
$e = $mw->Entry;
$e->insert(0, "abcdefg");
$e->destroy;
$mw->update;

$_->destroy for ($mw->children);
my $e1 = $mw->Entry(-fg => '#112233');
is(($mw->children)[0], $e1);
$e1->destroy;
is(scalar($mw->children), undef); # XXX why not 0?

$e = $mw->Entry(-font => $fixed, qw(-width 5 -bd 2 -relief sunken))->pack;
$e->insert(qw(0 012345678901234567890));
$e->xview(4);
$e->update;
is($e->index("end"), 21);

eval { $e->index("abogus") };
like($@, qr/bad entry index "abogus"/);

$e->selection(qw(from 1));
$e->selection(qw(to 6));
is($e->index(qw(anchor)), 1);

$e->selection(qw(from 4));
$e->selection(qw(to 1));
is($e->index(qw(anchor)), 4);

$e->selection(qw(from 3));
$e->selection(qw(to 15));
$e->selection(qw(adjust 4));
is($e->index(qw(anchor)), 15);

eval { $e->index("ebogus") };
like($@, qr/bad entry index "ebogus"/);

$e->icursor(2);
is($e->index('insert'), 2);

eval { $e->index("ibogus") };
like($@, qr/bad entry index "ibogus"/);

$e->selectionFrom(1);
$e->selectionTo(6);
is($e->index("sel.first"), 1);
is($e->index("sel.last"), 6);

$mw->SelectionClear($e);

if ($^O ne 'MSWin32') {
    # On unix, when selection is cleared, entry widget's internal
    # selection range is reset.

    eval { $e->index("sel.first") };
    like($@, qr/selection isn\'t in widget/);
    pass("Dummy to align number of tests with MSWin32");

} else {
    # On mac and pc, when selection is cleared, entry widget remembers
    # last selected range.  When selection ownership is restored to
    # entry, the old range will be rehighlighted.

    is($e->getSelected, '12345');
    is($e->index("sel.first"), 1);
}

if ($^O ne 'MSWin32') {
    eval { $e->index("sbogus") };
    like($@, qr/selection isn\'t in widget/);
} else {
    eval { $e->index("sbogus") };
    like($@, qr/bad entry index "sbogus"/);
}

eval { $e->index('@xyz') };
like($@, qr/bad entry index "\@xyz"/);

is($e->index('@4'), 4);
is($e->index('@11'), 4);
is($e->index('@12'), 5);
is($e->index('@' . ($e->width-6)), 8);
is($e->index('@' . ($e->width-5)), 9);
is($e->index('@1000'), 9);

eval { $e->index('1xyz') };
like($@, qr/bad entry index "1xyz"/);

is($e->index(-10), 0);
is($e->index(12), 12);
is($e->index(49), 21);

Tk::catch { $e->destroy };
$e = $mw->Entry(-show => ".");
$e->insert(qw(0 XXXYZZY));
$e->pack;
$e->update;
SKIP: {
    skip($skip_font_test, 2)
	if !$do_font_test;
    is($e->index('@7'), 0);
    is($e->index('@8'), 1);
}

# XXX Still need to write tests for EntryScanTo and EntrySelectTo.

$x = "";
for my $i (1 .. 500) {
    $x .= "This is line $i, out of 500\n";
}

Tk::catch { $e->destroy };
$e = $mw->Entry;
$e->insert(end => "This is a test string");
$e->selection(qw(from 1));
$e->selection(qw(to 18));
is($mw->SelectionGet, "his is a test str");

Tk::catch { $e->destroy };
$e = $mw->Entry(-show => "*");
$e->insert(end => "This is a test string");
$e->selection(qw(from 1));
$e->selection(qw(to 18));
is($mw->SelectionGet, "*****************");

Tk::catch { $e->destroy };
$e = $mw->Entry;
$e->insert("end", $x);
$e->selectionFrom(0);
$e->selectionTo("end");
is($mw->SelectionGet, $x);

Tk::catch { $e->destroy };
$e = $mw->Entry;
$e->insert(0, "Text");
$e->selectionFrom(0);
$e->selectionTo(4);
is($mw->SelectionGet, "Text");
$mw->SelectionClear;
$e->selectionFrom(0);
$e->selectionTo(4);
is($mw->SelectionGet, "Text");

# No tests for EventuallyRedraw.

Tk::catch {$e->destroy};
$e = $mw->Entry(qw(-width 10 -xscrollcommand), \&scroll)->pack;
$e->update;

$e->delete(qw(0 end));
$e->insert(qw(0 .............................));
SKIP: {
    skip($skip_wm_font_test, 1)
	if !$do_font_test || !$do_wm_test;
    is_deeply([map { substr($_, 0, 8) } $e->xview], [0, 0.827586]);
}

$e->delete(qw(0 end));
$e->insert(qw(0 XXXXXXXXXXXXXXXXXXXXXXXXXXXXX));
my $Xw = join(" ", map { substr($_, 0, 8) } $e->xview);

$e->configure(-show => 'X');
$e->delete(qw(0 end));
$e->insert(qw(0 .............................));
is(join(" ", map { substr($_, 0, 8) } $e->xview), $Xw);

$e->configure(-show => '.');
$e->delete(qw(0 end));
$e->insert(qw(0 XXXXXXXXXXXXXXXXXXXXXXXXXXXXX));
SKIP: {
    skip($skip_wm_font_test, 1)
	if !$do_font_test || !$do_wm_test;
    is_deeply([map { substr($_, 0, 8) } $e->xview], [0, 0.827586]);
}

$e->configure(-show => "");
$e->delete(qw(0 end));
is(($e->xview)[$_], $_) for (0 .. 1);

Tk::catch {$e->destroy};
$e = $mw->Entry(qw(-width 10 -xscrollcommand), \&scroll, -font => $fixed)->pack;
$e->update;

$e->delete(qw(0 end));
$e->insert(qw(0 123));
$e->update;
is(join(" ",@scrollInfo),"0 1");

$e->delete(qw(0 end));
$e->insert(qw(0 0123456789abcdef));
$e->xview(3);
$e->update;
SKIP: {
    skip($skip_wm_font_test, 1)
	if !$do_font_test || !$do_wm_test;
    is_deeply([@scrollInfo],[0.1875, 0.8125]);
}

$e->delete(qw(0 end));
$e->insert(qw(0 abcdefghijklmnopqrs));
$e->xview(6);
$e->update;
SKIP: {
    skip($skip_wm_font_test, 1)
	if !$do_font_test || !$do_wm_test;
    is_deeply([map { sprintf "%8f", $_ } @scrollInfo],[0.315789, 0.842105]);
}

Tk::catch {$e->destroy};
my $err;
eval {
    sub Tk::Error { $err = $_[1] }
    $e = $mw->Entry(qw(-width 5 -xscrollcommand thisisnotacommand))->pack;
    $e->update;
};
like($err, qr/Undefined subroutine &main::thisisnotacommand/, "Expected invalid -xscrollcommand callback");

#      pack .e
#      update
#      rename bgerror {}
#      list $x $errorInfo
#  } {{invalid command name "thisisnotacommand"} {invalid command name "thisisnotacommand"
#      while executing
#  "thisisnotacommand 0 1"
#      (horizontal scrolling command executed by entry)}}

## XXX no interp hidden with Perl/Tk?
#set l [interp hidden]
#eval destroy [winfo children .]
#  test entry-18.1 {Entry widget vs hiding} {
#      catch {destroy .e}
#      entry .e
#      interp hide {} .e
#      destroy .e
#      list [winfo children .] [interp hidden]
#  } [list {} $l]

######################################################################
# Additional tests

$e->validate; # check whether validate method is defined
pass("validate method seems to be defined");

__END__

