## 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; }