blob: c32331a3f8339d25a4f69973ef1cf1fb703b5ca0 [file] [log] [blame]
#
# ::iwidgets::Disjointlistbox
# ----------------------------------------------------------------------
# Implements a widget which maintains a disjoint relationship between
# the items displayed by two listboxes. The disjointlistbox is composed
# of 2 Scrolledlistboxes, 2 Pushbuttons, and 2 labels.
#
# The disjoint behavior of this widget exists between the two Listboxes,
# That is, a given instance of a ::iwidgets::Disjointlistbox will never
# exist which has Listbox widgets with items in common.
#
# Users may transfer items between the two Listbox widgets using the
# the two Pushbuttons.
#
# The options include the ability to configure the "items" displayed by
# either of the two Listboxes and to control the placement of the insertion
# and removal buttons.
#
# The following depicts the allowable "-buttonplacement" option values
# and their associated layout:
#
# "-buttonplacement" => center
#
# --------------------------
# |listbox| |listbox|
# | |________| |
# | (LHS) | button | (RHS) |
# | |========| |
# | | button | |
# |_______|--------|_______|
# | count | | count |
# --------------------------
#
# "-buttonplacement" => bottom
#
# ---------------------
# | listbox | listbox |
# | (LHS) | (RHS) |
# |_________|_________|
# | button | button |
# |---------|---------|
# | count | count |
# ---------------------
#
# ----------------------------------------------------------------------
# AUTHOR: John A. Tucker EMAIL: jatucker@spd.dsccc.com
#
# ======================================================================
#
# Default resources.
#
option add *Disjointlistbox.lhsLabelText Available widgetDefault
option add *Disjointlistbox.rhsLabelText Current widgetDefault
option add *Disjointlistbox.lhsButtonLabel {Insert >>} widgetDefault
option add *Disjointlistbox.rhsButtonLabel {<< Remove} widgetDefault
option add *Disjointlistbox.vscrollMode static widgetDefault
option add *Disjointlistbox.hscrollMode static widgetDefault
option add *Disjointlistbox.selectMode multiple widgetDefault
option add *Disjointlistbox.labelPos nw widgetDefault
option add *Disjointlistbox.buttonPlacement bottom widgetDefault
option add *Disjointlistbox.lhsSortOption increasing widgetDefault
option add *Disjointlistbox.rhsSortOption increasing widgetDefault
#
# Usual options.
#
itk::usual Disjointlistbox {
keep -background -textbackground -cursor \
-foreground -textfont -labelfont
}
# ----------------------------------------------------------------------
# ::iwidgets::Disjointlistbox
# ----------------------------------------------------------------------
itcl::class ::iwidgets::Disjointlistbox {
inherit itk::Widget
#
# options
#
itk_option define -buttonplacement buttonPlacement ButtonPlacement bottom
itk_option define -lhsbuttonlabel lhsButtonLabel LabelText {Insert >>}
itk_option define -rhsbuttonlabel rhsButtonLabel LabelText {<< Remove}
itk_option define -lhssortoption lhsSortOption LhsSortOption increasing
itk_option define -rhssortoption rhsSortOption RhsSortOption increasing
constructor {args} {}
#
# PUBLIC
#
public {
method clear {}
method getlhs {{first 0} {last end}}
method getrhs {{first 0} {last end}}
method lhs {args}
method insertlhs {items}
method insertrhs {items}
method setlhs {items}
method setrhs {items}
method rhs {args}
}
#
# PROTECTED
#
protected {
method insert {theListbox items}
method listboxClick {clickSide otherSide}
method listboxDblClick {clickSide otherSide}
method remove {theListbox items}
method showCount {}
method transfer {}
variable sourceListbox {}
variable destinationListbox {}
}
}
#
# Provide a lowercased access method for the ::iwidgets::Disjointlistbox class.
#
proc ::iwidgets::disjointlistbox {pathName args} {
uplevel ::iwidgets::Disjointlistbox $pathName $args
}
# ------------------------------------------------------------------
#
# Method: Constructor
#
# Purpose:
#
itcl::body ::iwidgets::Disjointlistbox::constructor {args} {
#
# Create the left-most Listbox
#
itk_component add lhs {
iwidgets::Scrolledlistbox $itk_interior.lhs \
-selectioncommand [itcl::code $this listboxClick lhs rhs] \
-dblclickcommand [itcl::code $this listboxDblClick lhs rhs]
} {
usual
keep -selectmode -vscrollmode -hscrollmode
rename -labeltext -lhslabeltext lhsLabelText LabelText
}
#
# Create the right-most Listbox
#
itk_component add rhs {
iwidgets::Scrolledlistbox $itk_interior.rhs \
-selectioncommand [itcl::code $this listboxClick rhs lhs] \
-dblclickcommand [itcl::code $this listboxDblClick rhs lhs]
} {
usual
keep -selectmode -vscrollmode -hscrollmode
rename -labeltext -rhslabeltext rhsLabelText LabelText
}
#
# Create the left-most item count Label
#
itk_component add lhsCount {
label $itk_interior.lhscount
} {
usual
rename -font -labelfont labelFont Font
}
#
# Create the right-most item count Label
#
itk_component add rhsCount {
label $itk_interior.rhscount
} {
usual
rename -font -labelfont labelFont Font
}
set sourceListbox $itk_component(lhs)
set destinationListbox $itk_component(rhs)
#
# Bind the "showCount" method to the Map event of one of the labels
# to keep the diplayed item count current.
#
bind $itk_component(lhsCount) <Map> [itcl::code $this showCount]
grid $itk_component(lhs) -row 0 -column 0 -sticky nsew
grid $itk_component(rhs) -row 0 -column 2 -sticky nsew
grid rowconfigure $itk_interior 0 -weight 1
grid columnconfigure $itk_interior 0 -weight 1
grid columnconfigure $itk_interior 2 -weight 1
eval itk_initialize $args
}
# ------------------------------------------------------------------
# Method: listboxClick
#
# Purpose: Evaluate a single click make in the specified Listbox.
#
itcl::body ::iwidgets::Disjointlistbox::listboxClick {clickSide otherSide} {
set button "button"
$itk_component($clickSide$button) configure -state active
$itk_component($otherSide$button) configure -state disabled
set sourceListbox $clickSide
set destinationListbox $otherSide
}
# ------------------------------------------------------------------
# Method: listboxDblClick
#
# Purpose: Evaluate a double click in the specified Listbox.
#
itcl::body ::iwidgets::Disjointlistbox::listboxDblClick {clickSide otherSide} {
listboxClick $clickSide $otherSide
transfer
}
# ------------------------------------------------------------------
# Method: transfer
#
# Purpose: Transfer source Listbox items to destination Listbox
#
itcl::body ::iwidgets::Disjointlistbox::transfer {} {
if {[$sourceListbox selecteditemcount] == 0} {
return
}
set selectedindices [lsort -integer -decreasing [$sourceListbox curselection]]
set selecteditems [$sourceListbox getcurselection]
foreach index $selectedindices {
$sourceListbox delete $index
}
foreach item $selecteditems {
$destinationListbox insert end $item
}
if {![string equal $itk_option(-${destinationListbox}sortoption) "none"]} {
$destinationListbox sort $itk_option(-${destinationListbox}sortoption)
}
showCount
}
# ------------------------------------------------------------------
# Method: getlhs
#
# Purpose: Retrieve the items of the left Listbox widget
#
itcl::body ::iwidgets::Disjointlistbox::getlhs {{first 0} {last end}} {
return [lhs get $first $last]
}
# ------------------------------------------------------------------
# Method: getrhs
#
# Purpose: Retrieve the items of the right Listbox widget
#
itcl::body ::iwidgets::Disjointlistbox::getrhs {{first 0} {last end}} {
return [rhs get $first $last]
}
# ------------------------------------------------------------------
# Method: insertrhs
#
# Purpose: Insert items into the right Listbox widget
#
itcl::body ::iwidgets::Disjointlistbox::insertrhs {items} {
remove $itk_component(lhs) $items
insert rhs $items
}
# ------------------------------------------------------------------
# Method: insertlhs
#
# Purpose: Insert items into the left Listbox widget
#
itcl::body ::iwidgets::Disjointlistbox::insertlhs {items} {
remove $itk_component(rhs) $items
insert lhs $items
}
# ------------------------------------------------------------------
# Method: clear
#
# Purpose: Remove the items from the Listbox widgets and set the item count
# Labels text to 0
#
itcl::body ::iwidgets::Disjointlistbox::clear {} {
lhs clear
rhs clear
showCount
}
# ------------------------------------------------------------------
# Method: insert
#
# Purpose: Insert the input items into the input Listbox widget while
# maintaining the disjoint property between them.
#
itcl::body ::iwidgets::Disjointlistbox::insert {theListbox items} {
set curritems [$theListbox get 0 end]
foreach item $items {
#
# if the item is not already present in the Listbox then insert it
#
if {[lsearch -exact $curritems $item] == -1} {
$theListbox insert end $item
}
}
if {![string equal $itk_option(-${theListbox}sortoption) "none"]} {
$theListbox sort $itk_option(-${theListbox}sortoption)
}
showCount
}
# ------------------------------------------------------------------
# Method: remove
#
# Purpose: Remove the input items from the input Listbox widget while
# maintaining the disjoint property between them.
#
itcl::body ::iwidgets::Disjointlistbox::remove {theListbox items} {
set indexes {}
set curritems [$theListbox get 0 end]
foreach item $items {
#
# if the item is in the listbox then add its index to the index list
#
if {[set index [lsearch -exact $curritems $item]] != -1} {
lappend indexes $index
}
}
foreach index [lsort -integer -decreasing $indexes] {
$theListbox delete $index
}
showCount
}
# ------------------------------------------------------------------
# Method: showCount
#
# Purpose: Set the text of the item count Labels.
#
itcl::body ::iwidgets::Disjointlistbox::showCount {} {
$itk_component(lhsCount) config -text "item count: [lhs size]"
$itk_component(rhsCount) config -text "item count: [rhs size]"
}
# ------------------------------------------------------------------
# METHOD: setlhs
#
# Set the items of the left-most Listbox with the input list
# option. Remove all (if any) items from the right-most Listbox
# which exist in the input list option to maintain the disjoint
# property between the two
#
itcl::body ::iwidgets::Disjointlistbox::setlhs {items} {
lhs clear
insertlhs $items
}
# ------------------------------------------------------------------
# METHOD: setrhs
#
# Set the items of the right-most Listbox with the input list
# option. Remove all (if any) items from the left-most Listbox
# which exist in the input list option to maintain the disjoint
# property between the two
#
itcl::body ::iwidgets::Disjointlistbox::setrhs {items} {
rhs clear
insertrhs $items
}
# ------------------------------------------------------------------
# Method: lhs
#
# Purpose: Evaluates the specified arguments against the lhs Listbox
#
itcl::body ::iwidgets::Disjointlistbox::lhs {args} {
return [eval $itk_component(lhs) $args]
}
# ------------------------------------------------------------------
# Method: rhs
#
# Purpose: Evaluates the specified arguments against the rhs Listbox
#
itcl::body ::iwidgets::Disjointlistbox::rhs {args} {
return [eval $itk_component(rhs) $args]
}
# ------------------------------------------------------------------
# OPTION: buttonplacement
#
# Configure the placement of the buttons to be either between or below
# the two list boxes.
#
itcl::configbody ::iwidgets::Disjointlistbox::buttonplacement {
if {$itk_option(-buttonplacement) != ""} {
if { [lsearch [component] lhsbutton] != -1 } {
eval destroy $itk_component(rhsbutton) $itk_component(lhsbutton)
}
if { [lsearch [component] bbox] != -1 } {
destroy $itk_component(bbox)
}
set where $itk_option(-buttonplacement)
switch $where {
center {
#
# Create the button box frame
#
itk_component add bbox {
frame $itk_interior.bbox
}
itk_component add lhsbutton {
button $itk_component(bbox).lhsbutton -command [itcl::code \
$this transfer]
} {
usual
rename -text -lhsbuttonlabel lhsButtonLabel LabelText
rename -font -labelfont labelFont Font
}
itk_component add rhsbutton {
button $itk_component(bbox).rhsbutton -command [itcl::code \
$this transfer]
} {
usual
rename -text -rhsbuttonlabel rhsButtonLabel LabelText
rename -font -labelfont labelFont Font
}
grid configure $itk_component(lhsCount) -row 1 -column 0 \
-sticky ew
grid configure $itk_component(rhsCount) -row 1 -column 2 \
-sticky ew
grid configure $itk_component(bbox) \
-in $itk_interior -row 0 -column 1 -columnspan 1 \
-sticky nsew
grid configure $itk_component(rhsbutton) \
-in $itk_component(bbox) -row 0 -column 0 -sticky ew
grid configure $itk_component(lhsbutton) \
-in $itk_component(bbox) -row 1 -column 0 -sticky ew
}
bottom {
itk_component add lhsbutton {
button $itk_interior.lhsbutton -command [itcl::code $this \
transfer]
} {
usual
rename -text -lhsbuttonlabel lhsButtonLabel LabelText
rename -font -labelfont labelFont Font
}
itk_component add rhsbutton {
button $itk_interior.rhsbutton -command [itcl::code $this \
transfer]
} {
usual
rename -text -rhsbuttonlabel rhsButtonLabel LabelText
rename -font -labelfont labelFont Font
}
grid $itk_component(lhsCount) -row 2 -column 0 -sticky ew
grid $itk_component(rhsCount) -row 2 -column 2 -sticky ew
grid $itk_component(lhsbutton) -row 1 -column 0 -sticky ew
grid $itk_component(rhsbutton) -row 1 -column 2 -sticky ew
}
default {
error "bad buttonplacement option\"$where\": should be center\
or bottom"
}
}
}
}
# ------------------------------------------------------------------
# OPTION: lhssortoption
#
# Configure the sort option to use for the left side
#
itcl::configbody ::iwidgets::Disjointlistbox::lhssortoption {
if {![string equal $itk_option(-lhssortoption) "none"]} {
$itk_component(lhs) sort $itk_option(-lhssortoption)
}
}
# ------------------------------------------------------------------
# OPTION: rhssortoption
#
# Configure the sort option to use for the right side
#
itcl::configbody ::iwidgets::Disjointlistbox::rhssortoption {
if {![string equal $itk_option(-rhssortoption) "none"]} {
$itk_component(rhs) sort $itk_option(-rhssortoption)
}
}