## edit_styles.pl
##
## demonstrates different edit styles within cells
##
## ewaldhei@idd.com
## This script uses tags and some logic to simulate check
## buttons, browseEntries, etc in cells. This approach is
## faster than using embedded windows, especially for large
## tables.
use Tk;
use Tk::TableMatrix;
main();
sub main
{
my $top = MainWindow->new;
my $_data = {};
my ($rows,$cols) = (12,7); # number of rows/cols
# create the table
my $t = $top->Scrolled
(TableMatrix =>
-rows => $rows, -cols => $cols,
-titlerows => 1, -titlecols => 1,
-width => 8, -height => 8 ,
-colwidth => 11,
-variable => $_data,
-cursor => 'top_left_arrow' ,
-borderwidth => 2 ,
-ipadx => 15,
-scrollbars => 'se',
)->pack(qw/-expand 1 -fill both/);
my $tm = $t->Subwidget('scrolled');
$tm->{columneditstyles} = {qw(1 readonly
2 editable
3 button
4 optionmenu
5 browseentry
6 checkbutton
)};
# set up tags for the various states of the buttons
$t->tagConfigure('OFF', -bg => 'gray60', -relief => 'raised');
$t->tagConfigure('ON', -bg => 'gray80', -relief => 'sunken');
$t->tagConfigure('sel', -bg => 'gray70', -relief => 'flat');
$t->tagConfigure('readonly', -relief => 'groove');
my %images = define_bitmaps($top);
$t->tagConfigure('optionmenu', -image => $images{optionmenu},
-anchor => 'e', -showtext => 1,
);
$t->tagConfigure('browseentry', -image => $images{browseentry},
-anchor => 'e', -showtext => 1);
$t->tagConfigure('checkbutton0', -image => $images{checkbutton0});
$t->tagConfigure('checkbutton1', -image => $images{checkbutton1});
$t->bind('<Key-Escape>' => \&end_edit);
# clean up if mouse leaves the widget
$t->bind('<FocusOut>',sub
{
my $w = shift;
$w->selectionClear('all');
$w->configure(-state => 'disabled');
});
# highlight the cell under the mouse
$t->bind('<Motion>', sub
{
my $w = shift;
my $Ev = $w->XEvent;
if( $w->selectionIncludes('@' . $Ev->x.",".$Ev->y)){
Tk->break;
}
$w->selectionClear('all');
$w->selectionSet('@' . $Ev->x.",".$Ev->y);
Tk->break;
## "break" prevents the call to TableMatrixCheckBorder
});
# mousebutton 1 edits the cell (or not) appropriately
$t->bind('<1>', sub
{
my ($w) = @_;
withdraw_edit_widgets($w);
my $Ev = $w->XEvent;
my ($x, $y) = ($Ev->x, $Ev->y);
my $rc = $w->index("\@$x,$y");
my $var = $w->cget(-var);
my ($r, $c) = split(/,/, $rc);
$r && $c || return;
$w->{_b1_row_col} = "$r,$c";
set_style_state($w);
my $style= $w->{columneditstyles}{$c} || 'editable';
if ($style eq 'optionmenu' || $style eq 'browseentry')
{
setup_toplevel_lbox($w, $r, $c);
}
elsif ($style eq 'button')
{
my $newval = $var->{$rc} =~ /ON/ ? 'OFF' : 'ON';
$var->{$rc} = $newval;
$w->tagCell($newval, $rc);
}
elsif ($style eq 'checkbutton')
{
$var->{$rc} = !$var->{$rc};
my $tag = $var->{$rc} ? 'checkbutton1' : 'checkbutton0';
$w->tagCell($tag, $rc);
}
});
# replace std b1-release
$t->bind('Tk::TableMatrix' => '<ButtonRelease-1>', \&set_style_state);
# inititialize the array, titles, and celltags
for (my $r = 0; $r < $rows; $r++)
{
for (my $c = 0; $c < $cols; $c++)
{
my $rc = "$r,$c";
if (!$r || !$c)
{
$_data->{$rc} = $r || $tm->{columneditstyles}{$c} || "";
}
else
{
$_data->{$rc} = $rc;
my $style = $tm->{columneditstyles}{$c} || 'editable';
if ($style eq 'readonly')
{
$t->tagCell('readonly', $rc);
}
if ($style eq 'optionmenu')
{
$_data->{$rc} = "$r options";
$t->tagCell('optionmenu', $rc);
}
elsif ($style eq 'browseentry')
{
$_data->{$rc} = "browse$r";
$t->tagCell('browseentry', $rc);
}
elsif ($style eq 'button')
{
$_data->{$rc} = $r % 4 ? 'ON' : 'OFF';
$t->tagCell($_data->{$rc}, $rc);
}
elsif ($style eq 'checkbutton')
{
$_data->{$rc} = $r % 3 ? 0 : 1;
$t->tagCell('checkbutton' . $_data->{$rc}, $rc);
}
}
}
}
Tk::MainLoop;
}
sub set_style_state
{
my ($w) = @_;
my ($r, $c) = split(/,/, $w->{_b1_row_col});
if (grep(!$w->{columneditstyles}{$c} || $_ eq $w->{columneditstyles}{$c},
qw(optionmenu readonly button checkbutton)))
{
$w->selectionClear('all');
$w->configure(state => 'disabled');
}
else
{
$w->configure(state => 'normal');
$w->activate($w->{_b1_row_col});
}
}
sub end_edit
{
my ($w) = @_;
$w->configure(-state => 'disabled');
$w->selectionClear('all');
}
sub setup_toplevel_lbox
{
my ($w, $r, $c) = @_;
my $toplevel = $w->{toplevel} ||=
$w->Toplevel(-bd => 2, -relief => 'raised');
my $lbox = $toplevel->{lbox};
$lbox->destroy() if $lbox;
$toplevel->overrideredirect(1);
my @options = map(chr(ord('A') + $_ - 1) x $_, 1..$r);
my $height = @options > 8 ? 8 : (@options || 1);
my $width = 2;
foreach (@options) { $width = length($_) if length($_) > $width; }
$lbox = $toplevel->{lbox} =
$toplevel->Scrolled
(Listbox =>
-height => $height,
-width => $width + 1,
-relief => 'raised',
-borderwidth => 1,
-highlightthickness => 0,
-bg => $w->cget('bg'),
-scrollbars => 'oe',
)->pack(-side => 'left');
$lbox->Subwidget('scrolled')->{_table_matrix} = $w;
$lbox->delete(0, 'end');
$lbox->insert(0, @options);
my ($gx, $gy) = ($w->rootx(), $w->rooty());
my @bbox = $w->bbox("$r,$c");
my ($mx, $my) = (int($gx + $bbox[0] + $bbox[2]), int($gy + $bbox[1]));
my $toplevel_ypixels = $height * $bbox[3]
+ $toplevel->cget("-bd") * 2 +
$toplevel->cget("-highlightthickness");
my $y2 = $my + $toplevel_ypixels;
$my = $w->vrootheight - $toplevel_ypixels if ($y2 > $w->vrootheight);
$toplevel->transient($w->toplevel());
$toplevel->geometry("+$mx+$my");
$toplevel->deiconify();
$toplevel->raise();
$lbox->bind('<ButtonRelease-1>',
sub {
my ($lbox) = @_;
my $i = $lbox->curselection();
my $val = $lbox->get($i);
my $w = delete $lbox->{_table_matrix};
my $rc = delete $w->{_b1_row_col};
my $var = $w->cget(-var);
$var->{$rc} = $val;
$w->set($rc => $val);
$w->selectionClear('all');
$w->configure(-state => 'disabled');
withdraw_edit_widgets($w);
}
);
}
sub withdraw_edit_widgets
{
my ($w) = @_;
my $toplevel = $w->{toplevel};
if ($toplevel && $toplevel->state eq 'normal')
{
$toplevel->withdraw();
}
}
#--------------------------------------------------------------
sub define_bitmaps
{
my ($w) = @_;
my $optionmenu =
'
/* XPM */
static char * xpm[] = {
"11 5 3 1",
" c None",
"+ c #D0D0D0",
"@ c #555555",
"+++++++++++",
"++++++++++@",
"++ @@",
"++@@@@@@@@@",
"+@@@@@@@@@@"};
';
my $browseentry =
'
/* XPM */
static char * xpm[] = {
"11 7 3 1",
" c None",
"+ c #D0D0D0",
"@ c #555555",
"+++++++++++",
"++++++++++@",
"+++ @@@",
" +++ @@@ ",
" +++ @@@ ",
" ++@@@ ",
" @@@ ",
};
';
my $cbutton0 =
'
/* XPM */
static char * xpm[] = {
"9 8 3 1",
" c None",
"@ c #B8B8B8",
"+ c #555555",
"+++++++++",
"++++++++@",
"++ @@",
"++ @@",
"++ @@",
"++ @@",
"++@@@@@@@",
"+@@@@@@@@"};
};
';
my $cbutton1 =
'
/* XPM */
static char * xpm[] = {
"9 8 4 1",
" c None",
"@ c #B8B8B8",
"+ c #555555",
". c #FF0000",
"+++++++++",
"++++++++@",
"++.....@@",
"++.....@@",
"++.....@@",
"++.....@@",
"++@@@@@@@",
"+@@@@@@@@"};
};
';
my %images;
$images{optionmenu} = $w->Pixmap('optionmenu', -data => $optionmenu);
$images{browseentry} = $w->Pixmap('browseentry', -data => $browseentry);
$images{checkbutton0} = $w->Pixmap('cbutton0', -data => $cbutton0);
$images{checkbutton1} = $w->Pixmap('cbutton1', -data => $cbutton1);
%images;
}