$Tk::ObjScanner::VERSION
=
'2.018'
;
require
5.006;
Tk::Widget->Construct(
'ObjScanner'
);
sub
scan_object {
import
Tk;
my
$object
=
shift
;
my
$animate
=
shift
|| 0;
my
$mw
= MainWindow->new;
$mw
->geometry(
'+10+10'
);
my
$s
=
$mw
->ObjScanner(
'-caller'
=>
$object
,
-destroyable
=> 1,
-title
=>
'object scan'
);
$s
->
pack
(
-expand
=> 1,
-fill
=>
'both'
);
$s
->OnDestroy(
sub
{
$mw
->destroy; } );
if
(
$animate
) {
$s
->_scan(
'root'
);
}
else
{
&MainLoop
;
}
}
sub
_scan {
my
$cw
=
shift
;
my
$topName
=
shift
;
$cw
->yview(
$topName
);
$cw
->
after
(200);
foreach
my
$c
(
$cw
->infoChildren(
$topName
) ) {
$cw
->displaySubItem(
$c
);
$cw
->_scan(
$c
);
}
$cw
->idletasks;
}
sub
_isa {
return
(reftype(
$_
[0]) ||
''
) eq
$_
[1] ;
}
sub
Populate {
my
(
$cw
,
$args
) =
@_
;
$cw
->{show_menu} =
defined
$args
->{
'show_menu'
} ?
delete
$args
->{
'show_menu'
}
:
defined
$args
->{
'-show_menu'
} ?
delete
$args
->{
'-show_menu'
}
: 0;
my
$display_show_tied_button
=
defined
$args
->{
'-show_tied'
}
||
defined
$args
->{show_tied} ? 0 : 1;
$cw
->{show_tied} =
defined
$args
->{
'-show_tied'
} ?
delete
$args
->{
'-show_tied'
}
:
defined
$args
->{show_tied} ?
delete
$args
->{show_tied}
: 1;
my
$scanned_data
=
delete
$args
->{
'caller'
} ||
delete
$args
->{
'-caller'
};
$cw
->{chief} = \
$scanned_data
;
my
$destroyable
=
defined
$args
->{
'-destroyable'
} ?
delete
$args
->{
'-destroyable'
}
:
defined
$args
->{
'destroyable'
} ?
delete
$args
->{
'destroyable'
}
: 1;
my
$destroy_label
=
delete
$args
->{
'-destroy_label'
} //
delete
$args
->{destroy_label} //
"destroy"
;
croak
"Missing caller argument in ObjScanner\n"
unless
defined
$cw
->{chief};
my
$title
=
delete
$args
->{title}
||
delete
$args
->{-title}
||
ref
(
$cw
->{chief} ) .
' scanner'
;
my
$background
=
delete
$args
->{
'background'
}
||
delete
$args
->{
'-background'
};
my
$selectbackground
=
delete
$args
->{
'selectbackground'
}
||
delete
$args
->{
'-selectbackground'
};
$cw
->{itemImg} =
delete
$args
->{
'itemImage'
}
||
delete
$args
->{
'-itemImage'
}
||
$cw
->Photo(
-file
=> Tk->findINC(
'textfile.xpm'
) );
$cw
->{foldImg} =
delete
$args
->{
'foldImage'
}
||
delete
$args
->{
'-foldImage'
}
||
$cw
->Photo(
-file
=> Tk->findINC(
'folder.xpm'
) );
$cw
->{openImg} =
delete
$args
->{
'openImage'
}
||
delete
$args
->{
'-openImage'
}
||
$cw
->Photo(
-file
=> Tk->findINC(
'openfolder.xpm'
) );
my
$menuframe
;
my
$menu
;
if
(
$destroyable
or
$cw
->{show_menu} ) {
$menuframe
=
$cw
->Frame(
-relief
=>
'raised'
,
-borderwidth
=> 1 )->
pack
(
-pady
=> 2,
-fill
=>
'x'
);
$menu
=
$cw
->{menu} =
$menuframe
->Menubutton(
-text
=>
$title
.
' menu'
)
->
pack
(
-fill
=>
'x'
,
-side
=>
'left'
);
$menu
->command(
-label
=>
'reload'
,
-command
=>
sub
{
$cw
->updateListBox; } );
}
my
%hlist_args
;
map
{
$hlist_args
{
$_
} =
delete
$args
->{
$_
}
if
defined
$args
->{
$_
}; }
qw/-columns -header/
;
my
$hlist
=
$cw
->Scrolled(
qw\HList -selectmode single -indent 35 -separator |
-itemtype imagetext -wideselection 0 \
,
%hlist_args
)->
pack
(
qw/-fill both -expand 1 /
);
$hlist
->
bind
(
'<Double-B1-ButtonRelease>'
=>
sub
{
my
$y
=
$Tk::event
->y;
my
$name
=
$Tk::widget
->nearest(
$y
);
$cw
->displaySubItem(
$name
, 0 );
} );
$hlist
->
bind
(
'<Double-B2-ButtonRelease>'
=>
sub
{
my
$y
=
$Tk::event
->y;
my
$name
=
$Tk::widget
->nearest(
$y
);
$cw
->displaySubItem(
$name
, 1 );
} )
if
$cw
->{show_tied};
$cw
->Advertise(
hlist
=>
$hlist
);
my
$popup
=
$cw
->{popup} =
$cw
->Toplevel;
$popup
->withdraw;
$cw
->{dumpLabel} =
$popup
->Label(
-text
=>
'not yet ...'
);
$cw
->{dumpLabel}->
pack
(
-fill
=>
'x'
);
$cw
->{dumpWindow} =
$popup
->Scrolled(
'ROText'
,
-height
=> 10 );
$cw
->{dumpWindow}->
pack
(
-fill
=>
'both'
,
-expand
=> 1 );
$popup
->Button(
-text
=>
'OK'
,
-command
=>
sub
{
$popup
->withdraw(); } )->
pack
;
$menu
->command(
-label
=>
$destroy_label
,
-command
=>
sub
{
$cw
->destroy; } )
if
defined
$cw
->{menu} &&
$destroyable
;
$cw
->ConfigSpecs(
-scrollbars
=> [
'DESCENDANTS'
,
undef
,
undef
,
'osoe'
],
-background
=> [
'DESCENDANTS'
,
'background'
,
'Background'
,
$background
],
-selectbackground
=> [
$hlist
,
'selectBackground'
,
'SelectBackground'
,
$selectbackground
],
-width
=> [
$hlist
,
undef
,
undef
, 80 ],
-height
=> [
$hlist
,
undef
,
undef
, 25 ],
-oldcursor
=> [
$hlist
,
undef
,
undef
,
undef
],
DEFAULT
=> [
$hlist
] );
$cw
->Delegates(
DEFAULT
=>
$hlist
);
$cw
->SUPER::Populate(
$args
);
if
(
defined
$menuframe
) {
$menuframe
->Checkbutton(
-text
=>
'show tied info'
,
-variable
=> \
$cw
->{show_tied},
-onvalue
=> 1,
-offvalue
=> 0,
-command
=>
sub
{
$cw
->updateListBox; }
)->
pack
(
-side
=>
'right'
)
if
$display_show_tied_button
;
}
$cw
->updateListBox;
return
$cw
;
}
sub
updateListBox {
my
$cw
=
shift
;
my
$h
=
$cw
->Subwidget(
'hlist'
);
my
$root
=
'root'
;
if
(
$h
->infoExists(
$root
) ) {
$h
->deleteOffsprings(
$root
);
$h
->entryconfigure(
$root
,
-text
=>
$cw
->element(
$cw
->{chief} ) );
}
else
{
$h
->add(
$root
,
-data
=> {
tied_display
=> 0,
item_ref
=>
$cw
->{chief} } );
$h
->itemCreate(
$root
, 0,
-image
=>
$cw
->{foldImg},
-text
=>
$cw
->element(
$cw
->{chief} ) );
}
$cw
->displaySubItem(
$root
, 0 );
}
sub
displaySubItem {
my
$cw
=
shift
;
my
$name
=
shift
;
my
$do_tie
=
shift
|| 0;
$do_tie
= 0
unless
$cw
->{show_tied};
my
$h
=
$cw
->Subwidget(
'hlist'
);
$h
->selectionClear();
$h
->selectionSet(
$name
);
my
$hash
=
$h
->info(
'data'
,
$name
);
my
$tied_display
=
$hash
->{tied_display};
my
$ref
=
$hash
->{item_ref};
my
$tied_object
;
if
( _isa(
$$ref
,
'ARRAY'
) ) {
$tied_object
=
tied
@
$$ref
; }
elsif
( _isa(
$$ref
,
'HASH'
) ) {
$tied_object
=
tied
%
$$ref
; }
elsif
( _isa(
$$ref
,
'REF'
) ) {
$tied_object
=
tied
$
$$ref
; }
else
{
$tied_object
=
tied
$$ref
; }
my
$is_tied
=
$do_tie
&&
defined
$tied_object
? 1 : 0;
my
$delete
=
$is_tied
^
$tied_display
;
if
(
$delete
) {
$hash
->{tied_display} =
$is_tied
;
$h
->deleteOffsprings(
$name
);
}
$cw
->toggle_display(
$name
);
return
if
scalar
(
$h
->infoChildren(
$name
) );
my
$ref_to_display
=
$is_tied
? \
$tied_object
:
$ref
;
$cw
->_swapCursor(
'watch'
);
$cw
->displayObject(
$name
,
$ref_to_display
);
$cw
->_swapCursor();
$cw
->_redisplayImage(
$name
);
}
sub
toggle_display {
my
$cw
=
shift
;
my
$name
=
shift
;
my
$h
=
$cw
->Subwidget(
'hlist'
);
foreach
my
$child
(
$h
->infoChildren(
$name
) ) {
if
(
$h
->info(
'hidden'
,
$child
) ) {
$h
->show(
'entry'
,
$child
); }
else
{
$h
->hide(
'entry'
,
$child
); }
}
$cw
->_redisplayImage(
$name
);
}
sub
displayObject {
my
$cw
=
shift
;
my
$name
=
shift
;
my
$ref
=
shift
;
my
$h
=
$cw
->Subwidget(
'hlist'
);
if
( _isa(
$$ref
,
'ARRAY'
)) {
foreach
my
$i
( 0 .. $
my
$img
=
ref
$$ref
->[
$i
] ?
$cw
->{foldImg} :
$cw
->{itemImg};
my
$npath
=
$h
->addchild(
$name
,
-data
=> {
tied_display
=> 0,
index
=>
$i
,
item_ref
=> \
$$ref
->[
$i
] } );
$h
->itemCreate(
$npath
, 0,
-image
=>
$img
,
-text
=>
$cw
->describe_element(
$ref
,
$i
) );
}
}
elsif
( _isa(
$$ref
,
'REF'
) or _isa(
$$ref
,
'SCALAR'
) ) {
my
$npath
=
$h
->addchild(
$name
,
-data
=> {
tied_display
=> 0,
item_ref
=>
$$ref
} );
$h
->itemCreate(
$npath
, 0,
-image
=> _isa(
$$ref
,
'REF'
) ?
$cw
->{foldImg} :
$cw
->{itemImg},
-text
=>
$cw
->describe_element(
$ref
) );
}
elsif
( _isa(
$$ref
,
'CODE'
) ) {
my
$deparse
= B::Deparse->new(
"-p"
,
"-sC"
);
my
$body
=
$deparse
->coderef2text(
$$ref
);
$cw
->popup_text(
"B::Deparse code dump"
,
$body
);
}
elsif
( _isa(
$$ref
,
'GLOB'
) ) {
if
( _isa(
$$ref
,
'UNIVERSAL'
) ) {
my
(
$what
) = (
$$ref
=~ /\b([A-Z]+)\b/ );
$cw
->popup_text(
'Error'
,
"Sorry, can't display a $what based $$ref object"
);
}
else
{
$cw
->popup_text(
'Error'
,
"Sorry, can't display "
.
$$ref
.
" reference"
);
}
}
elsif
( _isa(
$$ref
,
'HASH'
)) {
foreach
my
$k
(
sort
keys
%
$$ref
) {
my
$img
=
ref
(
$$ref
->{
$k
} ) ?
$cw
->{foldImg} :
$cw
->{itemImg};
my
$npath
=
$h
->addchild(
$name
,
-data
=> {
tied_display
=> 0,
index
=>
$k
,
item_ref
=> \
$$ref
->{
$k
} } );
$h
->itemCreate(
$npath
, 0,
-text
=>
$cw
->describe_element(
$ref
,
$k
),
-image
=>
$img
);
}
}
elsif
(
defined
$$ref
) {
$cw
->popup_text(
'scalar dump'
,
$$ref
)
if
$$ref
=~ /\n/;
}
}
sub
describe_element {
my
(
$cw
,
$ref
,
$index
) =
@_
;
if
( _isa(
$$ref
,
'ARRAY'
)) {
return
"[$index]-> "
.
$cw
->element( \
$$ref
->[
$index
] );
}
elsif
( _isa(
$$ref
,
'REF'
) or _isa(
$$ref
,
'SCALAR'
) ) {
return
$cw
->element(
$$ref
);
}
elsif
( _isa(
$$ref
,
'HASH'
)) {
return
(
"{$index}-> "
.
$cw
->element( \
$$ref
->{
$index
} ) );
}
else
{
die
"describe_element: unexpected type $$ref, index $index"
;
}
}
sub
popup_text {
my
(
$cw
,
$title
,
$text
) =
@_
;
$cw
->{popup}->title(
$title
);
$cw
->{dumpLabel}->configure(
-text
=>
$title
);
$cw
->{dumpWindow}->
delete
(
'1.0'
,
'end'
);
$cw
->{dumpWindow}->insert(
'end'
,
$text
);
$cw
->{popup}->deiconify;
$cw
->{popup}->raise;
}
sub
analyse_element {
my
$cw
=
shift
;
my
$ref
=
shift
;
my
%info
= (
description
=>
''
);
confess
"ref error"
unless
ref
(
$ref
);
$info
{element_ref} =
$ref
;
my
$str_ref
=
ref
(
$$ref
);
$info
{
tied
} =
$str_ref
eq
'HASH'
?
tied
%
$$ref
:
$str_ref
eq
'ARRAY'
?
tied
@
$$ref
:
$str_ref
eq
'SCALAR'
?
tied
$
$$ref
:
$str_ref
eq
'REF'
?
tied
$
$$ref
:
$str_ref
?
undef
:
tied
$$ref
;
if
( not
defined
$$ref
) {
$info
{description} =
'undefined'
;
}
elsif
(
$str_ref
and _isa(
$$ref
,
'UNIVERSAL'
) ) {
$info
{class} =
$str_ref
;
$info
{base} =
_isa(
$$ref
,
'SCALAR'
) ?
'SCALAR'
: (
$$ref
=~ /=([A-Z]+)\(/ ) ? $1
:
"some magic with $$ref"
;
$info
{description} =
"$str_ref OBJECT based on $info{base}"
;
}
elsif
(
$str_ref
) {
$info
{description} =
$str_ref
;
}
elsif
(
$$ref
=~ /\n/ ) {
$info
{description} =
'double click here to display value'
;
}
else
{
$info
{value} =
$$ref
;
}
if
(
defined
$$ref
) {
$info
{nb} =
_isa(
$$ref
,
'ARRAY'
) ?
scalar
(@
$$ref
)
: _isa(
$$ref
,
'HASH'
) ?
scalar
keys
(%
$$ref
)
:
undef
;
}
if
(
$str_ref
and isweak(
$$ref
) ) {
$info
{description} .=
' (weak ref)'
;
}
return
\
%info
;
}
sub
element {
my
$cw
=
shift
;
my
$ref
=
shift
;
my
$info
=
$cw
->analyse_element(
$ref
);
my
$what
=
$info
->{description} ||
"'$info->{value}'"
;
my
$nb
=
$info
->{nb};
my
$tied
=
$info
->{
tied
};
$what
.=
" ($nb)"
if
defined
$nb
;
$what
.=
" (tied with "
.
ref
(
$tied
) .
")"
if
defined
$tied
and
$cw
->{show_tied};
return
$what
;
}
sub
_swapCursor {
my
(
$cw
,
$cursor
) =
@_
;
my
$parent
=
$cw
->parent;
if
(
defined
(
$cursor
) ) {
$cw
->{oldcursor} =
$parent
->cget(
'-cursor'
);
$parent
->configure(
-cursor
=>
$cursor
);
}
else
{
$parent
->configure(
-cursor
=>
$cw
->{oldcursor} );
}
$parent
->update;
}
sub
_redisplayImage {
my
(
$cw
,
$name
) =
@_
;
my
$h
=
$cw
->Subwidget(
'hlist'
);
my
@children
=
$h
->infoChildren(
$name
);
return
if
@children
== 0;
my
$image
=
$h
->info(
'hidden'
,
$children
[0] ) ?
$cw
->{foldImg} :
$cw
->{openImg};
$h
->entryconfigure(
$name
,
'-image'
=>
$image
);
}
1;