blob: 3cd5c035639d5165d53702f5977f5299a9a0c0ba [file] [log] [blame]
# multibox.tcl - Multi-column listbox.
# Copyright (C) 1997 Cygnus Solutions.
# Written by Tom Tromey <tromey@cygnus.com>.
# FIXME:
# * Should support sashes so user can repartition widget sizes.
# * Should support itemcget, itemconfigure.
itcl_class Multibox {
# The selection mode.
public selectmode browse {
_apply_all configure [list -selectmode $selectmode]
}
# The height.
public height 10 {
_apply_all configure [list -height $height]
}
# This is a list of all the listbox widgets we've created. Private
# variable.
protected _listboxen {}
# Tricky: take the class bindings for the Listbox widget and turn
# them into Multibox bindings that directly run our bindings. That
# way any binding on any of our children will automatically work the
# right way.
# FIXME: this loses if any Listbox bindings are added later.
# To really fix we need Uhler's change to support megawidgets.
foreach seq [bind Listbox] {
regsub -all -- %W [bind Listbox $seq] {[winfo parent %W]} sub
bind Multibox $seq $sub
}
constructor {config} {
# The standard widget-making trick.
set class [$this info class]
set hull [namespace tail $this]
set old_name $this
::rename $this $this-tmp-
::frame $hull -class $class -relief flat -borderwidth 0
::rename $hull $old_name-win-
::rename $this $old_name
scrollbar [namespace tail $this].vs -orient vertical
bind [namespace tail $this].vs <Destroy> [list $this delete]
grid rowconfigure [namespace tail $this] 0 -weight 0
grid rowconfigure [namespace tail $this] 1 -weight 1
}
destructor {
destroy $this
}
#
# Our interface.
#
# Add a new column.
method add {args} {
# The first array set sets up the default values, and the second
# overwrites with what the user wants.
array set opts {-width 20 -fix 0 -title Zardoz}
array set opts $args
set num [llength $_listboxen]
listbox [namespace tail $this].box$num -exportselection 0 -height $height \
-selectmode $selectmode -width $opts(-width)
if {$num == 0} then {
[namespace tail $this].box$num configure -yscrollcommand [list [namespace tail $this].vs set]
[namespace tail $this].vs configure -command [list $this yview]
}
label [namespace tail $this].label$num -text $opts(-title) -anchor w
# No more class bindings.
set tag_list [bindtags [namespace tail $this].box$num]
set index [lsearch -exact $tag_list Listbox]
bindtags [namespace tail $this].box$num [lreplace $tag_list $index $index Multibox]
grid [namespace tail $this].label$num -row 0 -column $num -sticky new
grid [namespace tail $this].box$num -row 1 -column $num -sticky news
if {$opts(-fix)} then {
grid columnconfigure [namespace tail $this] $num -weight 0 \
-minsize [winfo reqwidth [namespace tail $this].box$num]
} else {
grid columnconfigure [namespace tail $this] $num -weight 1
}
lappend _listboxen [namespace tail $this].box$num
# Move the scrollbar over.
incr num
grid [namespace tail $this].vs -row 1 -column $num -sticky nsw
grid columnconfigure [namespace tail $this] $num -weight 0
}
method configure {config} {}
# FIXME: should handle automatically.
method cget {option} {
switch -- $option {
-selectmode {
return $selectmode
}
-height {
return $height
}
default {
error "option $option not supported"
}
}
}
# FIXME: this isn't ideal. But we want to support adding bindings
# at least. A "bind" method might be better.
method get_boxes {} {
return $_listboxen
}
#
# Methods that duplicate Listbox interface.
#
method activate index {
_apply_all activate [list $index]
}
method bbox index {
error "bbox method not supported"
}
method curselection {} {
return [_apply_first curselection {}]
}
# FIXME: In itcl 1.5, can't have a method name "delete". Sigh.
method delete_hack {args} {
_apply_all delete $args
}
# Return some contents. We return each item as a list of the
# columns.
method get {first {last {}}} {
if {$last == ""} then {
set r {}
foreach l $_listboxen {
lappend r [$l get $first]
}
return $r
} else {
# We do things this way so that we don't have to specially
# handle the index "end".
foreach box $_listboxen {
set seen(var-$box) [$box get $first $last]
}
# Tricky: we use the array indices as variable names and the
# array values as values. This lets us "easily" construct the
# result lists.
set r {}
eval foreach [array get seen] {{
set elt {}
foreach box $_listboxen {
lappend elt [set var-$box]
}
lappend r $elt
}}
return $r
}
}
method index index {
return [_apply_first index [list $index]]
}
# Insert some items. Each new item is a list of items for all
# columns.
method insert {index args} {
if {[llength $args]} then {
set seen(_) {}
unset seen(_)
foreach value $args {
foreach columnvalue $value lname $_listboxen {
lappend seen($lname) $columnvalue
}
}
foreach box $_listboxen {
eval $box insert $index $seen($box)
}
}
}
method nearest y {
return [_apply_first nearest [list $y]]
}
method scan {option args} {
_apply_all scan $option $args
}
method see index {
_apply_all see [list $index]
}
method selection {option args} {
if {$option == "includes"} then {
return [_apply_first selection [concat $option $args]]
} else {
return [_apply_all selection [concat $option $args]]
}
}
method size {} {
return [_apply_first size {}]
}
method xview args {
error "xview method not supported"
}
method yview args {
if {! [llength $args]} then {
return [_apply_first yview {}]
} else {
return [_apply_all yview $args]
}
}
#
# Private methods.
#
# This applies METHOD to every listbox.
method _apply_all {method argList} {
foreach l $_listboxen {
eval $l $method $argList
}
}
# This applies METHOD to the first listbox, and returns the result.
method _apply_first {method argList} {
set l [lindex $_listboxen 0]
return [eval $l $method $argList]
}
}