# Copyright 2007, 2008, 2009, 2010, 2011, 2012 Kevin Ryde
# This file is part of Gtk2-Ex-WidgetBits.
#
# Gtk2-Ex-WidgetBits is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Gtk2-Ex-WidgetBits is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Gtk2-Ex-WidgetBits. If not, see <http://www.gnu.org/licenses/>.
package Gtk2::Ex::TreeViewBits;
use 5.008;
use strict;
use warnings;
use Carp;
# uncomment this to run the ### lines
#use Smart::Comments;
our $VERSION = 48;
sub toggle_expand_row {
my ($treeview, $path, $open_all) = @_;
if ($treeview->row_expanded ($path)) {
$treeview->collapse_row ($path);
} else {
$treeview->expand_row ($path, $open_all);
}
}
sub remove_selected_rows {
my ($treeview) = @_;
my $model = $treeview->get_model;
# foreach converting path to rowref frees each path as converted, whereas
# a "map" keeps them all until the end, to perhaps save a couple of bytes
# of peak memory use.
#
my @rows = $treeview->get_selection->get_selected_rows; # paths
foreach (@rows) {
$_ = Gtk2::TreeRowReference->new($model,$_); # rowrefs
}
# shifting frees each rowref as it's processed, to save
# gtk_tree_row_ref_deleted() going through now removed rowrefs
#
while (my $rowref = shift @rows) {
my $path = $rowref->get_path || next; # if somehow gone away
if (my $iter = $model->get_iter ($path)) {
$model->remove ($iter);
} else {
carp 'Oops, selected row path "',$path->to_string,'" does not exist';
}
}
}
# In Gtk 2.12.12, when the row is bigger than the window, set_cursor()
# somehow likes to scroll to the opposite end of the row, presumably as a
# way of showing you the extents. So if already positioned at the start of
# the row then set_cursor() scrolls to the end of it. The scroll_to_cell()
# here moves back to the start of the row, but not before an unattractive
# bit of flashing. There doesn't seem any clean way to avoid that. It'd be
# much better if TreeView didn't draw immediately, but went through the
# queue_redraw / process_updates so as to collapse multiple programmatic
# changes.
#
sub scroll_cursor_to_path {
my ($treeview, $path) = @_;
### scroll_cursor_to_path() path: $path->to_string
my $model = $treeview->get_model || return; # nothing to make visible
# check path exists, in particular since ->scroll_to_cell() gives an
# unsightly warning if the path is invalid
$model->get_iter($path) or return;
$treeview->expand_to_path ($path);
$treeview->set_cursor ($path);
my $bin_window = $treeview->get_bin_window || return; # if unrealized
my ($bin_width, $bin_height) = $bin_window->get_size;
### $bin_height
my $rect = $treeview->get_cell_area ($path, undef);
### path: "y=".$rect->y." height=".$rect->height." end=".($rect->y + $rect->height)
if ($rect->y >= 0 && $rect->y + $rect->height <= $bin_height) {
### fully visible, don't scroll
return;
}
my $row_align = ($rect->height > $bin_height ? 0 : 0.5);
### scroll align to: $row_align
$treeview->scroll_to_cell ($path,
undef, # no column scroll
1, # use_align
$row_align,
0); # col_align
}
1;
__END__
=for stopwords TreeModel ListStore TreeStore TreeView Ryde Gtk2-Ex-WidgetBits Gtk2 Gtk
=head1 NAME
Gtk2::Ex::TreeViewBits - various helpers for Gtk2::TreeView
=head1 SYNOPSIS
use Gtk2::Ex::TreeViewBits;
=head1 FUNCTIONS
=over 4
=item C<< Gtk2::Ex::TreeViewBits::toggle_expand_row ($treeview, $path) >>
Toggle the row at C<$path> between expanded or collapsed. C<$path> is a
C<Gtk2::TreePath>.
This is a simple combination of check C<row_expanded> then either
C<expand_row> or C<collapse_row>. It's handy for making a toggle in the
style of the Space key (C<toggle-cursor-row>), but say on a button press
rather than the cursor row.
See F<examples/treeview-toggle-expand.pl> in the Gtk2-Ex-WidgetBits sources
for a complete program using this under a C<row-activate> signal.
=item C<< Gtk2::Ex::TreeViewBits::remove_selected_rows ($treeview) >>
Remove the currently selected rows of C<$treeview> from the underlying
TreeModel. If nothing is selected then do nothing.
Rows are removed using C<< $model->remove() >> as per C<Gtk2::ListStore> or
C<Gtk2::TreeStore>. The model doesn't have to be a ListStore or TreeStore,
only something with a compatible C<remove()> method.
Currently this is implemented by tracking rows to be removed using a
C<Gtk2::TreeRowReference> on each and removing them one by one. This isn't
fast, but is safe against additional changes to the model or the selection
during the removals.
=item C<< Gtk2::Ex::TreeViewBits::scroll_cursor_to_path ($treeview, $path) >>
Move the TreeView cursor to C<$path>, expanding and scrolling if necessary
to ensure the row is then visible.
This function is a combination of C<expand_row()>, C<set_cursor()> and
C<scroll_to_cell()>, except the scroll is skipped if C<$path> is already
fully visible. Avoiding a scroll is good because it avoids content jumping
around when merely moving to different nearby rows.
When a scroll is done the row is centred in the C<$treeview> window. If the
row is bigger than the window then it's positioned at the start of the
window.
=back
=head1 BUGS
As of Gtk 2.12.12, if a TreeView is in C<fixed-height-mode> and the last row
is unexpanded then a C<scroll_cursor_to_path()> to a sub-row of it doesn't
scroll correctly. It expands, moves the cursor, but the scroll goes only to
that last parent row, not the intended sub-row. Believe this is a bug in
Gtk.
=head1 SEE ALSO
C<Gtk2::TreeView>, C<Gtk2::Ex::WidgetBits>
=head1 HOME PAGE
L<http://user42.tuxfamily.org/gtk2-ex-widgetbits/index.html>
=head1 LICENSE
Copyright 2007, 2008, 2009, 2010, 2011, 2012 Kevin Ryde
Gtk2-Ex-WidgetBits is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any later
version.
Gtk2-Ex-WidgetBits is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
Gtk2-Ex-WidgetBits. If not, see L<http://www.gnu.org/licenses/>.
=cut