$VERSION
=
'0.03'
;
Construct Tk::Widget
'ListBrowser'
;
my
$dlmreg
=
qr/\.|\(|\)|\:|\!|\+|\,|\-|\<|\=|\>|\%|\&|\*|\"|\'|\/
|\;|\?|\[|\]|\^|\{|\||\}|\~|\\|\$|\@|\
my
%handlers
= (
bar
=>
'Bar'
,
column
=>
'Column'
,
list
=>
'List'
,
row
=>
'Row'
,
);
sub
Populate {
my
(
$self
,
$args
) =
@_
;
my
$nofilter
=
delete
$args
->{
'-nofilter'
};
$nofilter
=
''
unless
defined
$nofilter
;
$self
->SUPER::Populate(
$args
);
my
$canv
=
$self
->Scrolled(
'LBCanvas'
,
-keycall
=> [
'KeyPress'
,
$self
],
-scrollbars
=>
'osoe'
,
)->
pack
(
-expand
=> 1,
-fill
=>
'both'
);
my
$c
=
$canv
->Subwidget(
'scrolled'
);
$c
->configure(
-takefocus
=> 1);
$self
->Advertise(
'Canvas'
,
$c
);
$self
->
bind
(
'<Configure>'
, [
$self
,
'refresh'
]);
$c
->Tk::
bind
(
'<Button-1>'
, [
$self
,
'Button1'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<Control-Button-1>'
, [
$self
,
'Button1Control'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<Double-Button-1>'
, [
$self
,
'Button1Double'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<Shift-Button-1>'
, [
$self
,
'Button1Shift'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<Control-f>'
, [
$self
,
'filterFlip'
]);
$c
->Tk::
bind
(
'<Button-2>'
, [
$self
,
'Button2'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<B2-Motion>'
, [
$self
,
'Button2Motion'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<ButtonRelease-2>'
, [
$self
,
'Button2Release'
, Ev(
'x'
), Ev(
'y'
) ]);
$c
->Tk::
bind
(
'<Motion>'
, [
$self
,
'Motion'
, Ev(
'x'
), Ev(
'y'
) ]);
unless
(
$nofilter
) {
my
$filter
=
''
;
$self
->Advertise(
'Filter'
, \
$filter
);
my
$fframe
=
$self
->Frame;
$self
->Advertise(
'FilterFrame'
,
$fframe
);
my
$fentry
=
$fframe
->Entry(
-textvariable
=> \
$filter
,
)->
pack
(
-side
=>
'left'
,
-pady
=> 2,
-expand
=> 1,
-fill
=>
'x'
);
$self
->Advertise(
'FilterEntry'
,
$fentry
);
$fentry
->
bind
(
'<Control-f>'
, [
$self
,
'filterFlip'
]);
$fentry
->
bind
(
'<Escape>'
, [
$self
,
'filterFlip'
]);
$fentry
->
bind
(
'<Button-1>'
, [
$self
,
'filterClick'
]);
$fentry
->
bind
(
'<KeyRelease>'
, [
$self
,
'filterActivate'
]);
}
$self
->{ARRANGE} =
undef
;
$self
->{HANDLER} =
undef
;
$self
->{POOL} = [];
$self
->{ROWS} = 0;
$self
->{WRAPLENGTH} = 0;
$self
->ConfigSpecs(
-arrange
=> [
'METHOD'
,
undef
,
undef
,
'row'
],
-background
=> [
$c
,
'background'
,
'Background'
,
'#E8E8E8'
],
-browsecmd
=> [
'CALLBACK'
],
-command
=> [
'CALLBACK'
],
-filterdelay
=> [
'PASSIVE'
,
'filterDelay'
,
'FilterDelay'
, 300],
-filterfield
=> [
'PASSIVE'
,
undef
,
undef
,
'name'
],
-filteron
=> [
'PASSIVE'
,
undef
,
undef
,
''
],
-font
=> [
'PASSIVE'
,
'font'
,
'Font'
,
'Monotype 10'
],
-foreground
=> [
'PASSIVE'
,
'foreground'
,
'foreground'
,
'#3C3C3C'
],
-itemtype
=> [
'PASSIVE'
,
undef
,
undef
,
'imagetext'
],
-motionselect
=> [
'PASSIVE'
,
undef
,
undef
,
''
],
-selectbackground
=> [
'PASSIVE'
,
'selectBackground'
,
'SelectBackground'
,
'#A0A0FF'
],
-selectforeground
=> [
'PASSIVE'
,
'selectForeground'
,
'SelectForeground'
,
'#FAF9EA'
],
-selectmode
=> [
'PASSIVE'
,
undef
,
undef
,
'single'
],
-textanchor
=> [
'PASSIVE'
,
undef
,
undef
,
''
],
-textjustify
=> [
'PASSIVE'
,
undef
,
undef
,
'center'
],
-textside
=> [
'PASSIVE'
,
undef
,
undef
,
'bottom'
],
-wraplength
=> [
'METHOD'
,
undef
,
undef
, 0],
DEFAULT
=> [
$c
],
);
$self
->Delegates(
CanvasFocus
=>
$c
,
canvasx
=>
$c
,
canvasy
=>
$c
,
createImage
=>
$c
,
createRectangle
=>
$c
,
createText
=>
$c
,
xview
=>
$c
,
yview
=>
$c
,
xviewScroll
=>
$c
,
yviewScroll
=>
$c
,
DEFAULT
=>
$self
,
);
$self
->
after
(10,
sub
{
$self
->filterFlip
if
$self
->cget(
'-filteron'
) });
}
sub
_handler {
return
$_
[0]->{HANDLER} }
sub
add {
my
(
$self
,
$name
,
%options
) =
@_
;
if
(
$self
->infoExists(
$name
)) {
croak
"Entry '$name' already exists"
;
return
}
my
$after
=
delete
$options
{
'-after'
};
my
$before
=
delete
$options
{
'-before'
};
my
$item
= new Tk::ListBrowser::Item(
%options
,
-canvas
=>
$self
,
-name
=>
$name
,
);
my
$pool
=
$self
->pool;
if
(
defined
$after
) {
my
$index
=
$self
->
index
(
$after
);
splice
(
@$pool
,
$index
+ 1, 0,
$item
)
if
defined
$index
;
croak
"Entry for -after '$after' not found"
unless
defined
$index
;
}
elsif
(
defined
$before
) {
my
$index
=
$self
->
index
(
$before
);
splice
(
@$pool
,
$index
, 0,
$item
)
if
defined
$index
;
croak
"Entry for -before '$before' not found"
unless
defined
$index
;
}
else
{
push
@$pool
,
$item
}
return
$item
}
sub
anchorClear {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
for
(
@$pool
) {
$_
->anchor(0)
}
}
sub
anchorGet {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
for
(0 ..
@$pool
- 1) {
my
$obj
=
$pool
->[
$_
];
return
$obj
if
$obj
->anchored
}
return
undef
}
sub
anchorInitialize {
my
$self
=
shift
;
my
$i
=
$self
->anchorGet;
unless
(
defined
$i
) {
my
$name
=
$self
->infoFirstVisible;
$self
->anchorSet(
$name
)
unless
defined
$self
->anchorGet;
$self
->see(
$name
);
return
1
}
return
''
}
sub
anchorSet {
my
(
$self
,
$name
) =
@_
;
my
$item
=
$self
->get(
$name
);
if
((
defined
$item
) and (not
$item
->hidden)) {
$self
->anchorClear;
$item
->anchor(1);
return
1
}
return
''
}
sub
anchorSetColumnRow {
my
(
$self
,
$column
,
$row
) =
@_
;
my
$pool
=
$self
->pool;
my
$index
=
$self
->indexColumnRow(
$column
,
$row
);
if
(
defined
$index
) {
return
$self
->anchorSet(
$pool
->[
$index
]->name);
}
return
''
}
sub
arrange {
my
$self
=
shift
;
if
(
@_
) {
my
$arr
=
shift
;
my
$mod
=
$handlers
{
$arr
};
unless
(
defined
$mod
) {
croak
"Invalid handler $arr"
;
return
}
$self
->{ARRANGE} =
$arr
;
my
$modname
=
"Tk::ListBrowser::$mod"
;
my
$error
=
''
;
eval
"use $modname;"
;
$error
= $@;
unless
(
$error
) {
$self
->clear;
my
$h
=
$modname
->new(
$self
);
$self
->{HANDLER} =
$h
;
}
else
{
croak
$error
}
}
return
$self
->{ARRANGE}
}
sub
Button1 {
my
(
$self
,
$x
,
$y
) =
@_
;
my
$item
=
$self
->initem(
$x
,
$y
);
if
(
defined
$item
) {
$self
->selectionClear;
$item
->
select
(1);
$self
->anchorSet(
$item
->name);
$self
->Callback(
'-browsecmd'
,
$item
->name);
}
else
{
$self
->selectionClear;
}
}
sub
Button1Control {
my
(
$self
,
$x
,
$y
) =
@_
;
return
$self
->Button1(
$x
,
$y
)
if
$self
->cget(
'-selectmode'
) eq
'single'
;
my
$item
=
$self
->initem(
$x
,
$y
);
if
(
defined
$item
) {
if
(
$item
->selected) {
$item
->
select
(0)
}
else
{
$item
->
select
(1)
}
}
else
{
$self
->selectionClear;
}
}
sub
Button1Double {
my
(
$self
,
$x
,
$y
) =
@_
;
my
$item
=
$self
->initem(
$x
,
$y
);
if
(
defined
$item
) {
$self
->Callback(
'-command'
,
$item
->name);
}
else
{
$self
->selectionClear;
}
}
sub
Button1Shift {
my
(
$self
,
$x
,
$y
) =
@_
;
return
$self
->Button1(
$x
,
$y
)
if
$self
->cget(
'-selectmode'
) eq
'single'
;
my
$item
=
$self
->initem(
$x
,
$y
);
if
(
defined
$item
) {
my
$pool
=
$self
->pool;
my
@sel
=
$self
->selectionGet;
unless
(
@sel
) {
my
$start
=
$pool
->[0]->name;
$self
->selectionSet(
$pool
->[0]->name,
$item
->name);
return
}
if
(
$self
->
index
(
$item
->name) <
$self
->
index
(
$sel
[0])) {
$self
->selectionSet(
$item
->name,
$sel
[0]);
return
}
if
(
$self
->
index
(
$item
->name) >
$self
->
index
(
$sel
[
@sel
- 1])) {
$self
->selectionSet(
$sel
[
@sel
- 1],
$item
->name);
return
}
$self
->selectionClear;
}
}
sub
Button2 {
my
(
$self
,
$x
,
$y
) =
@_
;
$self
->configure(
-cursor
=>
'fleur'
);
$self
->{
'mouse_pos'
} = [
$x
,
$y
];
}
sub
Button2Motion {
my
(
$self
,
$x
,
$y
) =
@_
;
my
$mousepos
=
$self
->{
'mouse_pos'
};
my
(
$mx
,
$my
) =
@$mousepos
;
$self
->{
'mouse_pos'
} = [
$x
,
$y
];
my
$dx
=
$mx
-
$x
;
my
$dy
=
$my
-
$y
;
$self
->xviewScroll(-
$dx
,
'units'
)
if
$self
->_handler->scroll eq
'horizontal'
;
$self
->yviewScroll(-
$dy
,
'units'
)
if
$self
->_handler->scroll eq
'vertical'
;
}
sub
Button2Release {
my
$self
=
shift
;
$self
->configure(
-cursor
=>
'arrow'
);
delete
$self
->{
'mouse_pos'
};
}
sub
canvasSize {
my
$self
=
shift
;
my
$c
=
$self
->Subwidget(
'Canvas'
);
my
$offset
=
$c
->cget(
'-highlightthickness'
) +
$c
->cget(
'-borderwidth'
);
return
(
$c
->width -
$offset
,
$c
->height -
$offset
);
}
sub
clear {
my
$self
=
shift
;
$self
->anchorClear;
$self
->selectionClear;
my
$pool
=
$self
->pool;
grep
{
$_
->clear }
@$pool
;
my
$c
=
$self
->Subwidget(
'Canvas'
);
$c
->xview(
moveto
=> 0);
$c
->yview(
moveto
=> 0);
$c
->configure(
-scrollregion
=> [0, 0, 0, 0]);
}
sub
delete
{
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
$index
=
$self
->
index
(
$name
);
if
(
defined
$index
) {
my
(
$del
) =
splice
(
@$pool
,
$index
, 1);
$del
->clear;
return
}
croak
"Entry '$name' not found"
}
sub
deleteAll {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
grep
{
$self
->
delete
(
$_
->name) }
@$pool
;
$self
->clear;
}
my
%validconfigs
= (
-data
=> 1,
-hidden
=> 1,
-image
=> 1,
-text
=> 1
);
sub
entryCget {
my
(
$self
,
$name
,
$option
) =
@_
;
my
$i
=
$self
->get(
$name
);
unless
(
defined
$i
) {
croak
"Entry '$name' not found"
;
return
}
unless
(
exists
$validconfigs
{
$option
}) {
croak
"Invalid option '$option'"
;
return
}
$option
=~ s/^\-//;
return
$i
->
$option
}
sub
entryConfigure {
my
$self
=
shift
;
my
$name
=
shift
;
my
$i
=
$self
->get(
$name
);
unless
(
defined
$i
) {
croak
"Entry '$name' not found"
;
return
}
while
(
@_
) {
my
$option
=
shift
;
my
$value
=
shift
;
unless
(
exists
$validconfigs
{
$option
}) {
croak
"Invalid option '$option'"
;
return
}
$option
=~ s/^\-//;
$i
->
$option
(
$value
)
}
}
sub
filter {
my
(
$self
,
$filter
,
$value
) =
@_
;
return
1
if
$filter
eq
''
;
$filter
=
quotemeta
(
$filter
);
return
1
if
$value
eq
''
;
return
$value
=~ /
$filter
/i;
}
sub
filterClick {
my
$self
=
shift
;
my
$e
=
$self
->Subwidget(
'FilterEntry'
);
my
$text
=
$e
->get;
$e
->
delete
(0,
'end'
)
if
$text
eq
'Filter'
;
}
sub
filterActivate {
my
$self
=
shift
;
my
$filter_id
=
$self
->{
'filter_id'
};
if
(
defined
$filter_id
) {
$self
->afterCancel(
$filter_id
);
}
$filter_id
=
$self
->
after
(
$self
->cget(
'-filterdelay'
), [
'filterRefresh'
,
$self
]);
$self
->{
'filter_id'
} =
$filter_id
;
}
sub
filterFlip {
my
$self
=
shift
;
my
$f
=
$self
->Subwidget(
'FilterFrame'
);
if
(
defined
$f
) {
my
$e
=
$self
->Subwidget(
'FilterEntry'
);
if
(
$f
->ismapped) {
unless
(
$self
->cget(
'-filteron'
)) {
$f
->packForget;
$e
->
delete
(0,
'end'
);
$self
->CanvasFocus;
}
}
else
{
$e
->insert(
'end'
,
'Filter'
);
$f
->
pack
(
-fill
=>
'x'
);
}
}
}
sub
filterRefresh {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
my
$filter
=
$self
->Subwidget(
'FilterEntry'
)->get;
my
$filterfield
=
$self
->cget(
'-filterfield'
);
for
(
@$pool
) {
if
(
$self
->filter(
$filter
,
$_
->
$filterfield
)) {
$_
->hidden(
''
)
}
else
{
$_
->hidden(1)
}
}
delete
$self
->{
'filter_id'
};
$self
->refresh;
}
sub
focus {
$_
[0]->CanvasFocus }
sub
get {
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
@hit
=
grep
{
$_
->name eq
$name
}
@$pool
;
return
$hit
[0]
}
sub
getAll {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
return
@$pool
}
sub
getColumn {
my
(
$self
,
$col
) =
@_
;
my
$pool
=
$self
->pool;
my
@hits
=
grep
{ (
defined
$_
->column) and (
$_
->column eq
$col
) }
@$pool
;
return
@hits
}
sub
getIndex {
my
(
$self
,
$index
) =
@_
;
return
undef
unless
defined
$index
;
my
$pool
=
$self
->pool;
if
((
$index
< 0) or (
$index
>
@$pool
- 1)) {
croak
"Index '$index' out of range"
;
return
undef
;
}
return
$pool
->[
$index
];
}
sub
getRow {
my
(
$self
,
$row
) =
@_
;
my
$pool
=
$self
->pool;
my
@hits
=
grep
{ (
defined
$_
->row ) and (
$_
->row eq
$row
) }
@$pool
;
return
@hits
}
sub
hide {
my
(
$self
,
$name
) =
@_
;
my
$a
=
$self
->get(
$name
);
$a
->hidden(1)
if
defined
$a
}
sub
index
{
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
(
$index
) =
grep
{
$pool
->[
$_
]->name eq
$name
} 0 ..
@$pool
- 1;
return
$index
}
sub
indexColumnRow {
my
(
$self
,
$column
,
$row
) =
@_
;
my
$pool
=
$self
->pool;
my
(
$index
) =
grep
{
(
defined
$pool
->[
$_
]->column) and
(
defined
$pool
->[
$_
]->row) and
(
$pool
->[
$_
]->column eq
$column
) and
(
$pool
->[
$_
]->row eq
$row
)
} 0 ..
@$pool
- 1;
return
$index
}
sub
indexLast {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
my
$last
=
@$pool
- 1;
return
$last
}
sub
infoAnchor {
my
$self
=
shift
;
my
$a
=
$self
->anchorGet;
return
$a
->name
if
defined
$a
;
return
undef
}
sub
infoData {
my
(
$self
,
$name
) =
@_
;
my
$a
=
$self
->get(
$name
);
return
$a
->data
if
defined
$a
;
croak
"Entry '$name' not found"
;
return
undef
}
sub
infoExists {
my
(
$self
,
$name
) =
@_
;
my
$a
=
$self
->get(
$name
);
return
defined
$a
;
}
sub
infoFirst {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
return
undef
unless
@$pool
;
return
$pool
->[0]->name
}
sub
infoFirstVisible {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
for
(
@$pool
) {
return
$_
->name
unless
$_
->hidden
}
}
sub
infoHidden {
my
(
$self
,
$name
) =
@_
;
my
$a
=
$self
->get(
$name
);
if
(
defined
$a
) {
my
$flag
=
$a
->hidden;
$flag
=
''
if
$flag
eq 0;
return
$flag
}
croak
"Entry '$name' not found"
;
return
undef
}
sub
infoLast {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
return
undef
unless
@$pool
;
return
$pool
->[
@$pool
- 1]->name
}
sub
infoLastVisible {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
for
(
reverse
@$pool
) {
return
$_
->name
unless
$_
->hidden
}
}
sub
infoList {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
my
@list
;
for
(
@$pool
) {
push
@list
,
$_
->name }
return
@list
}
sub
infoNext {
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
$a
=
$self
->
index
(
$name
);
unless
(
defined
$a
) {
croak
"Entry '$name' not found"
;
return
}
return
undef
if
$a
eq
@$pool
- 1;
return
$pool
->[
$a
+ 1]->name;
}
sub
infoNextVisible {
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
$a
=
$self
->
index
(
$name
);
unless
(
defined
$a
) {
croak
"Entry '$name' not found"
;
return
}
for
(
$a
..
@$pool
- 1) {
return
$pool
->[
$_
]->name
unless
$pool
->[
$_
]->hidden
}
}
sub
infoPrev {
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
$a
=
$self
->
index
(
$name
);
unless
(
defined
$a
) {
croak
"Entry '$name' not found"
;
return
}
return
undef
if
$a
eq 0;
return
$pool
->[
$a
- 1]->name;
}
sub
infoPrevVisible {
my
(
$self
,
$name
) =
@_
;
my
$pool
=
$self
->pool;
my
$a
=
$self
->
index
(
$name
);
unless
(
defined
$a
) {
croak
"Entry '$name' not found"
;
return
}
for
(
reverse
0 ..
$a
) {
return
$pool
->[
$_
]->name
unless
$pool
->[
$_
]->hidden
}
}
sub
infoSelection {
return
$_
[0]->selectionGet }
sub
initem {
my
(
$self
,
$x
,
$y
) =
@_
;
$self
->CanvasFocus;
$x
=
int
(
$self
->canvasx(
$x
));
$y
=
int
(
$self
->canvasy(
$y
));
my
$pool
=
$self
->pool;
for
(
@$pool
) {
if
(
$_
->inregion(
$x
,
$y
)) {
return
$_
;
}
}
return
undef
}
sub
KeyArrowNavig {
my
(
$self
,
$dcol
,
$drow
) =
@_
;
return
undef
if
$self
->anchorInitialize;
my
$pool
=
$self
->pool;
my
$i
=
$self
->anchorGet;
my
$target
;
if
(
$drow
eq 0) {
my
$rown
=
$i
->row;
my
@row
=
$self
->getRow(
$rown
);
if
((
$dcol
> 0) and (
$i
->column >=
@row
- 1)) {
$target
=
$self
->moveRow(1);
}
elsif
((
$dcol
< 0) and (
$i
->column <= 0)) {
$target
=
$self
->moveRow(-1);
}
else
{
my
$ti
=
$self
->indexColumnRow(
$i
->column +
$dcol
,
$rown
);
$target
=
$self
->getIndex(
$ti
)
if
defined
$ti
;
}
}
else
{
my
$coln
=
$i
->column;
my
@column
=
$self
->getColumn(
$coln
);
if
((
$drow
> 0) and (
$i
->row >=
@column
- 1)) {
$target
=
$self
->moveColumn(1);
}
elsif
((
$drow
< 0) and (
$i
->row <= 0)) {
$target
=
$self
->moveColumn(-1);
}
else
{
my
$ti
=
$self
->indexColumnRow(
$coln
,
$i
->row +
$drow
);
$target
=
$self
->getIndex(
$ti
)
if
defined
$ti
;
}
}
if
(
defined
$target
) {
my
$name
=
$target
->name;
$self
->anchorSet(
$name
);
$self
->see(
$name
);
return
1
}
return
''
}
sub
KeyArrowSelect {
my
(
$self
,
$dcol
,
$drow
) =
@_
;
return
if
$self
->anchorInitialize;
my
$p
=
$self
->anchorGet;
if
(
$self
->KeyArrowNavig(
$dcol
,
$drow
)) {
my
$new
=
$self
->anchorGet->name;
if
(
$p
->selected) {
$self
->selectionSet(
$new
)
}
else
{
$self
->selectionUnSet(
$new
)
}
}
}
sub
KeyLastColumn {
my
$self
=
shift
;
return
if
$self
->anchorInitialize;
my
$i
=
$self
->anchorGet;
my
$row
=
$i
->row;
my
$col
=
$self
->lastColumnInRow(
$row
);
unless
(
$self
->anchorSetColumnRow(
$col
,
$row
)) {
my
$flag
=
''
;
while
((not
$flag
) and (
$col
>= 0)) {
$col
--;
my
$index
=
$self
->indexColumnRow(
$col
,
$row
);
my
$name
=
$self
->pool->[
$index
]->name;
$flag
=
$self
->anchorSet(
$name
);
$self
->see(
$name
)
if
$flag
;
}
}
}
sub
KeyPress {
my
(
$self
,
$key
) =
@_
;
my
$pool
=
$self
->pool;
my
$h
=
$self
->_handler;
return
unless
@$pool
;
my
@sel
=
$self
->selectionGet;
if
(
$key
eq
'Return'
) {
return
if
$self
->anchorInitialize;
my
$i
=
$self
->anchorGet;
my
$name
=
$i
->name;
$self
->selectionSet(
$name
);
$self
->Callback(
'-command'
,
$name
);
return
}
if
(
$key
eq
'Escape'
) {
if
(
$self
->Subwidget(
'FilterEntry'
)->ismapped) {
$self
->filterFlip
}
else
{
$self
->selectionClear;
$self
->anchorClear;
}
return
}
if
(
$key
eq
'Down'
) {
$self
->KeyArrowNavig(0, 1);
return
}
if
(
$key
eq
'End'
) {
$self
->KeyLastColumn;
return
}
if
(
$key
eq
'Control-End'
) {
my
$name
=
$self
->infoLastVisible;
$self
->see(
$name
);
$self
->
after
(50,
sub
{
$self
->anchorSet(
$name
) });
return
}
if
(
$key
eq
'Home'
) {
return
if
$self
->anchorInitialize;
my
$i
=
$self
->anchorGet;
my
$row
=
$i
->row;
my
$index
=
$self
->indexColumnRow(0,
$row
);
my
$name
=
$pool
->[
$index
]->name;
$self
->anchorSet(
$name
);
$self
->see(
$name
);
return
}
if
(
$key
eq
'Control-Home'
) {
my
$name
=
$self
->infoFirstVisible;
$self
->anchorSet(
$name
);
$self
->see(
$name
);
return
}
if
(
$key
eq
'Left'
) {
$self
->KeyArrowNavig(-1, 0);
return
}
if
(
$key
eq
'Right'
) {
$self
->KeyArrowNavig(1, 0);
return
}
if
(
$key
eq
'Up'
) {
$self
->KeyArrowNavig(0, -1);
return
}
if
(
$key
eq
'space'
) {
return
if
$self
->anchorInitialize;
my
$i
=
$self
->anchorGet;
my
$name
=
$i
->name;
$self
->selectionFlip(
$name
);
$self
->Callback(
'-browsecmd'
,
$name
)
if
$i
->selected;
return
}
if
(
$key
eq
'Shift-Down'
) {
return
$self
->KeyArrowSelect(0, 1)
}
if
(
$key
eq
'Shift-End'
) {
return
if
$self
->anchorInitialize;
my
$i
=
$self
->anchorGet;
my
$column
=
$i
->column;
my
$row
=
$i
->row;
my
@items
=
$self
->getRow(
$row
);
if
(
$self
->KeyLastColumn) {
for
(
@items
) {
if
(
$self
->cget(
'-selectmode'
) eq
'multiple'
) {
$self
->selectionFlip(
$_
->name)
}
}
}
}
if
(
$key
eq
'Control-Shift-End'
) {
return
if
$self
->anchorInitialize;
my
$begin
=
$self
->anchorGet;
my
$name
=
$self
->infoLastVisible;
if
(
$self
->anchorSet(
$name
)) {
my
$end
=
$self
->anchorGet;
if
(
$begin
->selected) {
$self
->selectionClear
if
$self
->cget(
'-selectmode'
) eq
'single'
;
$self
->selectionSet(
$begin
->name,
$end
->name);
}
else
{
$self
->selectionClear
if
$self
->cget(
'-selectmode'
) eq
'single'
;
$self
->selectionUnSet(
$begin
->name,
$end
->name);
}
$self
->see(
$name
);
}
}
if
(
$key
eq
'Shift-Home'
) {
return
if
$self
->anchorInitialize;
my
$i
=
$self
->anchorGet;
my
$column
=
$i
->column;
my
$row
=
$i
->row;
my
@items
=
$self
->getRow(
$row
);
if
(
$self
->anchorSetColumnRow(0,
$row
)) {
for
(
@items
) {
$self
->selectionFlip(
$_
->name)
}
}
return
}
if
(
$key
eq
'Control-Shift-Home'
) {
return
if
$self
->anchorInitialize;
my
$begin
=
$self
->anchorGet;
if
(
$self
->anchorSet(
$self
->infoFirstVisible)) {
my
$end
=
$self
->anchorGet;
if
(
$begin
->selected) {
$self
->selectionSet(
$begin
->name,
$end
->name);
}
else
{
$self
->selectionUnSet(
$begin
->name,
$end
->name);
}
}
return
}
if
(
$key
eq
'Shift-Left'
) {
return
$self
->KeyArrowSelect(-1, 0)
}
if
(
$key
eq
'Shift-Right'
) {
return
$self
->KeyArrowSelect(1, 0)
}
if
(
$key
eq
'Shift-Up'
) {
return
$self
->KeyArrowSelect(0, -1)
}
}
sub
lastColumnInRow {
my
(
$self
,
$row
) =
@_
;
my
$pool
=
$self
->pool;
my
@row
=
$self
->getRow(
$row
);
return
$row
[
@row
- 1]->column;
}
sub
lastRowInColumn {
my
(
$self
,
$column
) =
@_
;
my
$pool
=
$self
->pool;
my
@column
=
$self
->getColumn(
$column
);
return
$column
[
@column
- 1]->row;
}
sub
Motion {
my
(
$self
,
$x
,
$y
) =
@_
;
return
unless
$self
->cget(
'-selectmode'
) eq
'single'
;
return
unless
$self
->cget(
'-motionselect'
);
my
$item
=
$self
->initem(
$x
,
$y
);
if
(
defined
$item
) {
$self
->selectionSet(
$item
->name);
}
}
sub
moveColumn {
my
(
$self
,
$delta
) =
@_
;
my
$i
=
$self
->anchorGet;
my
$column
=
$i
->column;
my
$row
=
$i
->row;
my
@c
=
$self
->getColumn(
$column
);
my
$lastrow
=
@c
- 1;
$row
=
$row
+
$delta
;
if
(
$row
>=
$lastrow
) {
$column
++;
$row
= 0;
}
elsif
(
$column
<= 0) {
$column
--;
my
@nc
=
$self
->getColumn(
$column
);
$row
=
@nc
- 1;
}
my
$target
;
my
$index
=
$self
->indexColumnRow(
$column
,
$row
);
$target
=
$self
->getIndex(
$index
)
if
defined
$index
;
return
$target
;
}
sub
moveRow {
my
(
$self
,
$delta
) =
@_
;
my
$i
=
$self
->anchorGet;
my
$column
=
$i
->column;
my
$row
=
$i
->row;
my
@r
=
$self
->getRow(
$row
);
my
$lastcolumn
=
@r
- 1;
$column
=
$column
+
$delta
;
if
(
$column
>=
$lastcolumn
) {
$column
= 0;
$row
++;
}
elsif
(
$column
<= 0) {
$row
--;
my
@nr
=
$self
->getRow(
$row
);
$column
=
@nr
- 1;
}
my
$target
;
my
$index
=
$self
->indexColumnRow(
$column
,
$row
);
$target
=
$self
->getIndex(
$index
)
if
defined
$index
;
return
$target
;
}
sub
pool {
return
$_
[0]->{POOL} }
sub
refreshTimer {
my
$self
=
shift
;
delete
$self
->{
'timer_id'
};
$self
->refresh(1);
}
sub
refresh {
my
(
$self
,
$timer
) =
@_
;
if
(
my
$id
=
$self
->{
'timer_id'
}) {
$self
->afterCancel(
$id
);
my
$nid
=
$self
->
after
(50, [
'refreshTimer'
,
$self
]);
$self
->{
'timer_id'
} =
$nid
;
}
unless
(
defined
$timer
) {
my
$id
=
$self
->
after
(50, [
'refreshTimer'
,
$self
]);
$self
->{
'timer_id'
} =
$id
;
return
}
$self
->_handler->refresh;
}
sub
see {
my
(
$self
,
$name
) =
@_
;
my
$scrollregion
=
$self
->cget(
'-scrollregion'
);
return
unless
@$scrollregion
;
my
(
$cx1
,
$cy1
,
$cx2
,
$cy2
) =
@$scrollregion
;
my
$i
=
$self
->get(
$name
);
my
(
$cwidth
,
$cheight
) =
$self
->canvasSize;
my
(
$ix1
,
$iy1
,
$ix2
,
$iy2
) =
$i
->region;
my
$h
=
$self
->_handler;
if
(
$h
->scroll eq
'horizontal'
) {
my
(
$vl
,
$vr
) =
$self
->xview;
my
$div
=
$cx2
-
$cx1
;
if
((
$div
> 0) and (
$ix1
/
$div
<
$vl
)) {
$self
->xview(
moveto
=>
$ix1
/
$div
);
}
elsif
((
$div
> 0) and (
$ix2
/
$div
>
$vr
)) {
my
$mr
= (
$ix2
-
$cwidth
+ 2)/
$div
;
$self
->xview(
moveto
=>
$mr
);
}
}
if
(
$h
->scroll eq
'vertical'
) {
my
(
$vt
,
$vb
) =
$self
->yview;
my
$div
=
$cy2
-
$cy1
;
if
((
$div
> 0) and (
$iy1
/
$div
<
$vt
)) {
$self
->yview(
moveto
=>
$iy1
/
$div
);
}
elsif
((
$div
> 0) and (
$iy2
/
$div
>
$vb
)){
my
$mr
= (
$iy2
-
$cheight
+ 2)/
$div
;
$self
->yview(
moveto
=>
$mr
);
}
}
}
sub
selectAll {
my
$self
=
shift
;
return
if
$self
->cget(
'-selectmode'
) eq
'single'
;
my
$pool
=
$self
->pool;
grep
{
$_
->
select
}
@$pool
;
}
sub
selectionClear {
my
$self
=
shift
;
my
$pool
=
$self
->pool;
grep
{
$_
->
select
(0) }
@$pool
;
}
sub
selectionFlip {
my
(
$self
,
$begin
,
$end
) =
@_
;
(
$begin
,
$end
) =
$self
->selectionIndex(
$begin
,
$end
);
my
$pool
=
$self
->pool;
for
(
$begin
..
$end
) {
my
$i
=
$pool
->[
$_
];
if
(
$i
->selected) {
$self
->selectionClear
if
$self
->cget(
'-selectmode'
) eq
'single'
;
$i
->
select
(0);
}
else
{
$self
->selectionClear
if
$self
->cget(
'-selectmode'
) eq
'single'
;
$i
->
select
;
}
}
}
sub
selectionGet {
my
$self
=
shift
;
my
@list
;
my
$pool
=
$self
->pool;
for
(
@$pool
) {
push
@list
,
$_
->name
if
$_
->selected }
return
@list
;
}
sub
selectionIndex {
my
(
$self
,
$begin
,
$end
) =
@_
;
$end
=
$begin
unless
defined
$end
;
$begin
=
$self
->
index
(
$begin
);
$end
=
$self
->
index
(
$end
);
if
(
$begin
>
$end
) {
my
$t
=
$begin
;
$begin
=
$end
;
$end
=
$t
;
}
return
(
$begin
,
$end
)
}
sub
selectionSet {
my
(
$self
,
$begin
,
$end
) =
@_
;
(
$begin
,
$end
) =
$self
->selectionIndex(
$begin
,
$end
);
my
$pool
=
$self
->pool;
for
(
$begin
..
$end
) {
my
$i
=
$pool
->[
$_
];
unless
(
$i
->hidden) {
$self
->selectionClear
if
$self
->cget(
'-selectmode'
) eq
'single'
;
$i
->
select
}
}
}
sub
selectionUnSet {
my
(
$self
,
$begin
,
$end
) =
@_
;
$end
=
$begin
unless
defined
$end
;
(
$begin
,
$end
) =
$self
->selectionIndex(
$begin
,
$end
);
my
$pool
=
$self
->pool;
for
(
$begin
..
$end
) {
my
$i
=
$pool
->[
$_
];
$i
->
select
(0)
unless
$i
->hidden;
}
}
sub
show {
my
(
$self
,
$name
) =
@_
;
my
$a
=
$self
->get(
$name
);
$a
->hidden(0)
if
defined
$a
}
sub
textFormat {
my
(
$self
,
$text
) =
@_
;
my
$wraplength
=
$self
->cget(
'-wraplength'
);
my
$font
=
$self
->cget(
'-font'
);
return
$text
if
$wraplength
<= 0;
my
@lines
=
split
(/\n/,
$text
);
my
@out
;
for
(
@lines
) {
my
$line
=
$_
;
my
$length
=
$self
->fontMeasure(
$font
,
$line
);
if
(
$length
>
$wraplength
) {
my
$res
=
$length
/
length
(
$line
);
my
$oklength
=
int
(
$wraplength
/
$res
);
while
(
length
(
$line
) >
$oklength
) {
my
$t
=
substr
(
$line
, 0,
$oklength
,
''
);
if
(
$t
=~ s/([
$dlmreg
])([^
$dlmreg
]+$)//) {
$line
=
"$2$line"
;
$t
=
"$t$1"
;
}
push
@out
,
$t
;
}
push
@out
,
$line
;
}
else
{
push
@out
,
$line
;
}
}
my
$result
=
''
;
while
(
@out
) {
$result
=
$result
.
shift
@out
;
$result
=
"$result\n"
if
@out
}
return
$result
}
sub
textHeight {
my
(
$self
,
$text
) =
@_
;
return
0
if
$text
eq
''
;
my
$height
= 1;
while
(
$text
=~ /\n/g) {
$height
++ }
my
$font
=
$self
->cget(
'-font'
);
return
(
$height
*
$self
->fontMetrics(
$font
,
'-linespace'
))
}
sub
textWidth {
my
(
$self
,
$text
) =
@_
;
return
$self
->fontMeasure(
$self
->cget(
'-font'
),
$text
)
unless
$text
=~ /\n/;
my
$width
= 0;
while
(
$text
=~ s/^([^\n]*)\n//) {
my
$w
=
$self
->fontMeasure(
$self
->cget(
'-font'
), $1);
$width
=
$w
if
$w
>
$width
;
}
if
(
$text
ne
''
) {
my
$w
=
$self
->fontMeasure(
$self
->cget(
'-font'
),
$text
);
$width
=
$w
if
$w
>
$width
;
}
return
$width
}
sub
wraplength {
my
$self
=
shift
;
if
(
@_
) {
my
$l
=
shift
;
if
(
$l
> 0) {
$l
= 40
if
$l
< 40;
}
$self
->{WRAPLENGTH} =
$l
;
}
return
$self
->{WRAPLENGTH}
}
1;