App-Chart
view release on metacpan or search on metacpan
lib/App/Chart/Gtk2/Ex/ListOfListsModel.pm view on Meta::CPAN
}
}
$self->{'mlist'} = \@mlist;
$self->{'n_columns'} = $n_columns;
$self->{'column_types'} = \@column_types;
if (DEBUG) {
local $, = ' ';
print " column_types",@column_types,"\n";
}
}
# gtk_tree_model_get_flags
#
sub GET_FLAGS {
return [];
}
# gtk_tree_model_get_n_columns
#
sub GET_N_COLUMNS {
my ($self) = @_;
if (DEBUG >= 2) { print "ListOfListsModel get_n_columns\n"; }
return $self->{'n_columns'};
}
# gtk_tree_model_get_column_type
#
sub GET_COLUMN_TYPE {
return $_[0]->{'column_types'}->[$_[1]];
}
# gtk_tree_model_get_iter
#
sub GET_ITER {
my ($self, $path) = @_;
if (DEBUG >= 2) { print "ListOfListsModel get_iter, path='",
$path->to_string,"'\n"; }
my $depth = $path->get_depth;
$self->{'list_model'} || return undef; # when no models
my ($top_index, $sub_index) = $path->get_indices;
if ($depth == 1) {
# top-level
return [ $self->{'stamp'}, $top_index, undef, undef ];
}
if ($depth == 2) {
# sub-list
my $minfo = $self->{'mlist'}->[$top_index] || return undef;
return [ $self->{'stamp'}, $sub_index, $minfo, undef ];
}
return undef;
}
# gtk_tree_model_get_path
#
sub GET_PATH {
my ($self, $iter) = @_;
if (DEBUG >= 2) { print "ListOfListsModel get_path\n"; }
my ($index, $minfo) = _iter_validate ($self, $iter);
if (defined $minfo) {
my $mnum = $minfo->{'mnum'};
return Gtk2::TreePath->new_from_indices ($mnum, $index);
} else {
return Gtk2::TreePath->new_from_indices ($index);
}
}
# gtk_tree_model_get_value
#
sub GET_VALUE {
my ($self, $iter, $col) = @_;
if (DEBUG >= 2) { print "ListOfListsModel get_value\n";
print " iter=",$iter->[0],",",$iter->[1],",",
defined $iter->[2] ? $iter->[2] : 'undef',
" col=$col\n"; }
my ($index, $minfo) = _iter_validate ($self, $iter);
my $list_model_n_columns = $self->{'list_model_n_columns'};
my $model;
if ($col < $list_model_n_columns) {
# columns of list_model
$model = $self->{'list_model'} || return undef;
if ($minfo) {
# $iter is a submodel row
$index = $minfo->{'mnum'};
}
} else {
# columns of submodels
if (! $minfo) {
if (DEBUG >= 2) { print " submodel col empty in toplevel row\n"; }
return;
}
$col -= $list_model_n_columns;
$model = $minfo->{'model'} || return undef;
if (DEBUG >= 2) { print " submodel adj col=$col\n"; }
}
my $subiter = $model->iter_nth_child(undef,$index) || do {
if (DEBUG >= 2) { print " cannot get subiter $model index=$index\n"; }
return undef;
};
return $model->get_value ($subiter, $col);
}
# gtk_tree_model_iter_next
#
sub ITER_NEXT {
my ($self, $iter) = @_;
if (DEBUG >= 2) { print "ListOfListsModel iter_next\n"; }
my ($index, $minfo) = _iter_validate ($self, $iter);
$index++;
if ($minfo) {
# next in submodel
my $model = $minfo->{'model'} || return undef;
if ($index < $model->iter_n_children(undef)) {
return [ $self->{'stamp'}, $index, $minfo, undef ];
}
} else {
# next in toplevel
if ($index < scalar @{$self->{'mlist'}}) {
return [ $self->{'stamp'}, $index, undef, undef ];
}
}
return undef;
}
# gtk_tree_model_iter_children
#
sub ITER_CHILDREN {
my ($self, $iter) = @_;
if (DEBUG) { print "ListOfListsModel iter_children\n"; }
return ITER_NTH_CHILD ($self, $iter, 0);
}
# gtk_tree_model_iter_has_child
#
sub ITER_HAS_CHILD {
my ($self, $iter) = @_;
if (DEBUG) { print "ListOfListsModel has_child ",$iter->[1],"\n"; }
# Note: prior to Gtk2-Perl 1.190 the return had to be a number, not any
# old boolean
return ITER_N_CHILDREN($self,$iter) != 0;
}
# gtk_tree_model_iter_n_children
#
sub ITER_N_CHILDREN {
my ($self, $iter) = @_;
if (DEBUG) { print "ListOfListsModel iter_n_children\n"; }
if (! defined $iter) {
# asking about top-levels
if (DEBUG) { print " toplevel has ",scalar @{$self->{'mlist'}},"\n"; }
return scalar @{$self->{'mlist'}};
}
my ($index, $minfo) = _iter_validate ($self, $iter);
if ($minfo) {
# asking about under submodel
if (DEBUG) { print " nothing under submodel rows\n"; }
return 0;
} else {
# asking about under toplevel row
$minfo = $self->{'mlist'}->[$index] || return 0;
my $model = $minfo->{'model'} || return 0;
if (DEBUG) { print " model row has",$model->iter_n_children(undef),"\n"; }
return $model->iter_n_children(undef);
}
}
# gtk_tree_model_iter_nth_child
#
sub ITER_NTH_CHILD {
my ($self, $iter, $n) = @_;
if (DEBUG) { print "ListOfListsModel iter_nth_child",
" index=",$iter?$iter->[1]:'<iter undef>',
" minfo=",($iter&&$iter->[2])||'undef',
" child n=$n\n"; }
my $mlist = $self->{'mlist'};
if (defined $iter) {
my ($index, $minfo) = _iter_validate ($self, $iter);
if (! $minfo) {
# $n'th row of model under toplevel $index
if ($minfo = $mlist->[$index]) {
if (my $model = $minfo->{'model'}) {
if ($n < $model->iter_n_children(undef)) {
if (DEBUG) { print " yes, submodel $model row $n\n"; }
return [ $self->{'stamp'}, $n, $minfo, undef ];
}
}
}
}
} else {
# $n'th row of top-level
if ($n < @$mlist) {
if (DEBUG) { print " yes, toplevel row $n\n"; }
return [ $self->{'stamp'}, $n, undef, undef ];
}
}
if (DEBUG) { print " no\n"; }
return undef;
}
# gtk_tree_model_iter_parent
#
sub ITER_PARENT {
my ($self, $iter) = @_;
if (DEBUG) { print "ListOfListsModel iter_parent\n"; }
my ($index, $minfo) = _iter_validate ($self, $iter);
if (defined $minfo) {
if (DEBUG) { print " yes, up to toplevel ",$minfo->{'mnum'},"\n"; }
return [ $self->{'stamp'}, $minfo->{'mnum'}, undef, undef ];
}
if (DEBUG) { print " no\n"; }
return undef;
}
# gtk_tree_model_ref_node
# gtk_tree_model_unref_node
#------------------------------------------------------------------------------
# our iters
# return ($mnum, $minfo), with $minfo undef on toplevel rows
sub _iter_validate {
my ($self, $iter) = @_;
if ($iter->[0] != $self->{'stamp'}) {
croak "iter is not for this ", ref($self),
" (stamp ", $iter->[0], " want ", $self->{'stamp'}, ")\n";
}
return ($iter->[1], $iter->[2]);
}
sub _top_index_to_iterobj {
my ($self, $mnum) = @_;
return Gtk2::TreeIter->new_from_arrayref ([ $self->{'stamp'}, $mnum, undef, undef ]);
}
sub _sub_index_to_iterobj {
my ($self, $minfo, $index) = @_;
return Gtk2::TreeIter->new_from_arrayref ([ $self->{'stamp'}, $index, $minfo, undef]);
}
sub _iterobj_validate {
my ($self, $iterobj) = @_;
if (! defined $iterobj) {
croak 'iter is undef';
}
my $iter = Gtk2::TreeIter->to_arrayref ($self->{'stamp'});
return ($iter->[1], $iter->[2]);
}
#------------------------------------------------------------------------------
# 'list_model' toplevel signals
# 'row-changed' on the toplevel list-model
#
sub _do_toplevel_row_changed {
my ($model, $path, $subiter, $ref_weak_self) = @_;
my $self = $$ref_weak_self || return;
if (DEBUG) { print "ListOfListsModel toplevel row_changed handler\n";}
if ($path->get_depth != 1) { return; } # ignore non-toplevel
my ($mnum) = $path->get_indices;
my $old_minfo = $self->{'mlist'}->[$mnum];
my $old_model = $old_minfo && $old_minfo->{'model'};
my $old_has_child = $old_minfo && $old_minfo->{'has_child'};
$old_minfo = undef;
_update_models ($self);
my $new_minfo = $self->{'mlist'}->[$mnum];
my $new_model = $new_minfo->{'model'};
my $iterobj = _top_index_to_iterobj ($self, $mnum);
$self->row_changed ($path, $iterobj);
if (! defined $old_has_child ||
$old_has_child != $new_minfo->{'has_child'}) {
$self->row_has_child_toggled ($path, $iterobj);
}
if (($old_model||0) != ($new_model||0)) {
if (DEBUG) { print " different model at $mnum: old ",
$old_model||'undef', " new ",$new_model||'undef',"\n";}
my $old_len = $old_model ? $old_model->iter_n_children(undef) : 0;
my $new_len = $new_model ? $new_model->iter_n_children(undef) : 0;
my $changed_len = min ($old_len, $new_len);
if (DEBUG) { print " changed_len $changed_len\n"; }
foreach my $i (0 .. $changed_len - 1) {
$path = Gtk2::TreePath->new_from_indices ($mnum, $i);
$iterobj = _sub_index_to_iterobj ($self, $new_minfo, $i);
$self->row_changed ($path, $iterobj);
}
if ($old_len > $changed_len) {
if (DEBUG) { print " deleted to $old_len\n"; }
$path = Gtk2::TreePath->new_from_indices ($mnum, $changed_len);
foreach ($changed_len .. $old_len - 1) {
$self->row_deleted ($path);
}
lib/App/Chart/Gtk2/Ex/ListOfListsModel.pm view on Meta::CPAN
#
sub _do_sublist_row_deleted {
my ($model, $subpath, $minfo) = @_;
my $self = $minfo->{'self'} || return;
if (DEBUG) { print "ListOfListsModel submodel row_deleted handler\n";}
if ($subpath->get_depth != 1) { return; } # ignore non-toplevel
my $mnum = $minfo->{'mnum'};
my $path = $subpath->copy;
$path->prepend_index ($mnum);
$self->row_deleted ($path);
if ($minfo->{'has_child'} && $model->iter_n_children(undef) == 0) {
# newly become no-children
$minfo->{'has_child'} = 0;
$path->up;
my $iterobj = _top_index_to_iterobj ($self, $mnum);
$self->row_has_child_toggled ($path, $iterobj);
}
}
# 'rows-reordered' on sub-list
#
sub _do_sublist_rows_reordered {
my ($model, $subpath, $subiter, $aref, $minfo) = @_;
my $self = $minfo->{'self'} || return;
if (DEBUG) { print "ListOfListsModel submodel rows_reordered handler\n";}
if ($subpath->get_depth != 0) { return; } # ignore non-toplevel
my $mnum = $minfo->{'mnum'};
my $path = Gtk2::TreePath->new_from_indices ($mnum);
my $iterobj = _top_index_to_iterobj ($self, $mnum);
$self->rows_reordered ($path, $iterobj, @$aref);
}
#------------------------------------------------------------------------------
# Gtk2::TreeStore compatible methods
# gtk_tree_store_append
# gtk_tree_store_prepend
sub append {
push @_, 'append';
goto _Xpend;
}
sub prepend {
push @_, 'prepend';
goto _Xpend;
}
sub _Xpend {
my ($self, $parent_iterobj, $method) = @_;
my ($model, $minfo, $subiter);
if (! defined $parent_iterobj) {
# append to toplevel
$model = $self->{'list_model'};
$subiter = $model->$method;
} else {
my $index;
($index, $minfo) = _iterobj_validate ($parent_iterobj);
if ($minfo) { croak 'cannot append under sub-row'; }
# append to submodel, ie. under a toplevel row
$minfo = $self->{'mlist'}->[$index] || croak 'no such row (bad iter)';
$model = $minfo->{'model'};
$subiter = $model->$method;
}
if (! $subiter) { return undef; }
my ($index) = $model->get_path($subiter)->get_indices;
return Gtk2::TreeIter->new_from_arrayref ([ $self->{'stamp'}, $index, $minfo, undef]);
}
# gtk_tree_store_is_ancestor
sub is_ancestor {
my ($self, $parent_iterobj, $child_iterobj) = @_;
my ($parent_index, $parent_minfo) = _iterobj_validate ($parent_iterobj);
if ($parent_minfo) { return 0; } # a submodel row
my ($child_index, $child_minfo) = _iterobj_validate ($child_iterobj);
return defined $child_minfo; # only if a submodel row
}
# gtk_tree_store_iter_depth
sub iter_depth {
my ($self, $iterobj) = @_;
my ($index, $minfo) = _iterobj_validate ($iterobj);
return $minfo ? 2 : 1;
}
# gtk_tree_store_clear
sub clear {
my ($self) = @_;
my $list_model = $self->{'list_model'} || return;
$list_model->clear;
}
# gtk_tree_store_iter_is_valid
# this is not quite right
# if $iter->[2] is bogus it probably causes a segv when treated as an RV
sub iter_is_valid {
my ($self, $iterobj) = @_;
my $iter = eval { $iterobj->to_arrayref($self->{'stamp'}) };
my $index = $iter->[1];
my $minfo = $iter->[2];
if (defined $minfo) {
return List::Util::first {$_ == $minfo} @{$self->{'mlist'}}
&& $index < $minfo->{'model'}->iter_n_children(undef);
} else {
return $index < scalar @{$self->{'mlist'}};
}
}
sub remove {
my ($self, $iterobj) = @_;
my ($index, $minfo) = _iterobj_validate ($iterobj);
my $model = $minfo ? $minfo->{'model'} : $self->{'list_model'};
my $subiter = $model->iter_nth_child (undef, $index)
|| croak 'no such row (bad iter)';
if ($model->remove ($subiter)) {
# more rows
$iterobj->set([$self->{'stamp'}, $index+1, $minfo, undef]);
return 1;
} else {
$iterobj->set([0,0,undef,undef]); # zap
return 0;
}
}
# gtk_tree_store_reorder
# Gtk2::TreeStore reorder() taking multiple args
sub reorder {
my ($self, $iterobj, @order) = @_;
if ($iterobj) {
my ($index, $minfo) = _iterobj_validate ($iterobj);
if ($minfo) {
croak 'nothing under sub-row';
}
$minfo = $self->{'mlist'}->[$index] || croak 'iter out of range';
my $model = $minfo->{'model'} || croak 'no submodel';
$model->reorder (@order);
} else {
# toplevel
$self->{'list_model'}->reorder (@order);
}
}
#------------------------------------------------------------------------------
# other methods
sub path_for_model {
my ($self, $model) = @_;
my $mlist = $self->{'mlist'};
foreach my $minfo (@$mlist) {
my $this_model = $minfo->{'model'} || next;
if ($model == $this_model) {
return Gtk2::TreePath->new_from_indices ($minfo->{'mnum'});
}
}
return undef;
}
#------------------------------------------------------------------------------
# drag source
# gtk_tree_drag_source_row_draggable ($self, $path)
#
sub ROW_DRAGGABLE {
unshift @_, 'row_draggable';
goto &_drag_source;
}
# gtk_tree_drag_source_drag_data_delete ($self, $path)
#
sub DRAG_DATA_DELETE {
unshift @_, 'drag_data_delete';
goto &_drag_source;
}
# gtk_tree_drag_source_drag_data_get
#
sub DRAG_DATA_GET {
my ($self, $path, $sel) = @_;
unshift @_, 'drag_data_get'; # needing Gtk2 1.200
goto &_drag_source;
}
sub _drag_source {
my ($method, $self, $path, @sel) = @_;
if (DEBUG) { print "ListOfLists \U$method\E path=",$path->to_string,"\n"; }
my ($model, $subpath) = _drag_path ($self, $path)
( run in 1.025 second using v1.01-cache-2.11-cpan-39bf76dae61 )