if 0 {
MBS 2008-01-28 For an application that I've been developing at work, I needed some sort of disjointlistbox. The Iwidgets package offers one of these, in the form of disjointlistbox. There was also A little list selector by RS and tklib has swaplist.
None of these were quite right for what my user's were requesting.
I ended up with a combination of swaplist with tablelist.
Sorry, no docs for it at present time. I based a lot of the code on swaplist, so hopefully between the demo code and the docs for both tablelist and swaplist you should get the idea.
I'm sure that it could use some polish, so comments, improvements, etc. are welcome.
Let me know what you think... --Mark
}
set ::useTestDemo 1 ;# Used to enable the demo code below set ::useTile 1 ;# Set this in the calling routine before ;# "package require Swaptablelist" if you ;# want to enable "Tile" ############################## Cut Here ############################## if {![info exists ::useTile]} { set ::useTile 0 } package require Tk if {$::useTile} { package require tile package require Tablelist_tile } else { package require Tablelist } package require BWidget package require autoscroll package provide Swaptablelist 1.0 namespace eval Swaptablelist { variable lTbl variable rTbl namespace export swaptablelist } ######################################################################## option add *Tablelist.activeStyle frame option add *Tablelist.background gray98 option add *Tablelist.stripeBackground #e0e8f0 option add *Tablelist.setGrid yes option add *Tablelist.movableColumns yes option add *Tablelist.labelCommand tablelist::sortByColumn option add *Tablelist.Font "Courier -12" ######################################################################## proc Swaptablelist::create_tablelist {fr args} { array set options { -height 0 -width 0 } Swaptablelist::parseOpts options {\ {-title {}} \ {-listvariable {}} \ {-llistvar {}} {-rlistvar {}} \ {-columns {}} \ {-lcolumns {}} {-rcolumns {}} \ {-height {}} {-width {}} \ {-lwidth {}} {-rwidth {}} \ {-llabel {}} {-rlabel {}} \ {-reorder boolean} \ {-geometry {}} \ {-lbuttontext {}} {-rbuttontext {}} \ {-ubuttontext {}} {-dbuttontext {}}} \ $args tablelist::tablelist $fr.tbl \ -columns $options(-columns) \ -listvariable $options(-listvariable) \ -editstartcommand editStartCmd \ -editendcommand editEndCmd \ -xscrollcommand "$fr.xs set" \ -yscrollcommand "$fr.ys set" \ -stretch all \ -selectmode extended \ -height $options(-height) \ -width $options(-width) scrollbar $fr.ys -orient v -command "$fr.tbl yview" scrollbar $fr.xs -orient h -command "$fr.tbl xview" grid $fr.tbl -row 0 -column 0 -sticky nsew grid $fr.ys -row 0 -column 1 -sticky ns grid $fr.xs -row 2 -column 0 -sticky ew grid columnconfigure $fr 0 -weight 1 grid columnconfigure $fr 1 -weight 0 grid rowconfigure $fr 0 -weight 1 grid rowconfigure $fr 1 -weight 0 update idletasks ::autoscroll::autoscroll $fr.ys ::autoscroll::autoscroll $fr.xs return $fr.tbl } proc Swaptablelist::create_swap_list {w args} { variable lTbl variable rTbl array set options { -reorder 1 -lbuttontext "<<" -rbuttontext ">>" -ubuttontext "Move Up" -dbuttontext "Move Down" -lcolumns "Left" -rcolumns "Right" -height 0 -lwidth 0 -rwidth 0 } Swaptablelist::parseOpts options {\ {-title {}} \ {-listvariable {}} \ {-llistvar {}} {-rlistvar {}} \ {-lcolumns {}} {-rcolumns {}} \ {-height {}} {-lwidth {}} {-rwidth {}} \ {-llabel {}} {-rlabel {}} \ {-reorder boolean} \ {-geometry {}} \ {-lbuttontext {}} {-rbuttontext {}} \ {-ubuttontext {}} {-dbuttontext {}}} \ $args # Left hand side... if {$::useTile} { # set lf1 [LabelFrame $w.f1 -text $options(-llabel) -side top ] set lf1 [ttk::labelframe $w.f1 -text $options(-llabel) -labelanchor nw] } else { set lf1 [labelframe $w.f1 -text $options(-llabel) -labelanchor nw] } # set f1 [$lf1 getframe] set f1 $lf1 set options(-columns) [Swaptablelist::extract_column $options(-lcolumns)] set options(-width) $options(-lwidth) set options(-listvariable) $options(-llistvar) set lTbl($w) [eval [list Swaptablelist::create_tablelist $f1] [array get options]] # Right hand side... if {$::useTile} { # set lf2 [LabelFrame $w.f2 -text $options(-rlabel) -side top ] set lf2 [ttk::labelframe $w.f2 -text $options(-rlabel) -labelanchor nw] } else { set lf2 [labelframe $w.f2 -text $options(-rlabel) -labelanchor nw] } # set f2 [$lf2 getframe] set f2 $lf2 set options(-columns) [Swaptablelist::extract_column $options(-rcolumns)] set options(-width) $options(-rwidth) set options(-listvariable) $options(-rlistvar) set rTbl($w) [eval [list Swaptablelist::create_tablelist $f2] [array get options]] # left/right buttons set flr [frame $w.lr] set width [Swaptablelist::strmin 5 $options(-lbuttontext) $options(-rbuttontext)] set olist [list] if {$::useTile} { ttk::button $flr.left -width $width -text $options(-lbuttontext) \ -command [list Swaptablelist::ShiftL $w $olist] ttk::button $flr.right -width $width -text $options(-rbuttontext) \ -command [list Swaptablelist::ShiftR $w $olist] } else { button $flr.left -width $width -text $options(-lbuttontext) \ -command [list Swaptablelist::ShiftL $w $olist] button $flr.right -width $width -text $options(-rbuttontext) \ -command [list Swaptablelist::ShiftR $w $olist] } grid $flr.right -pady 4 grid $flr.left -pady 4 grid columnconfigure $flr 0 -uniform 1 # up/down buttons set width [Swaptablelist::strmin 3 $options(-ubuttontext) $options(-dbuttontext)] set fud [frame $w.ud] if {$::useTile} { ttk::button $fud.up -width $width -text $options(-ubuttontext) \ -command [list Swaptablelist::ShiftUD $w u] ttk::button $fud.down -width $width -text $options(-dbuttontext) \ -command [list Swaptablelist::ShiftUD $w d] } else { button $fud.up -width $width -text $options(-ubuttontext) \ -command [list Swaptablelist::ShiftUD $w u] button $fud.down -width $width -text $options(-dbuttontext) \ -command [list Swaptablelist::ShiftUD $w d] } pack $fud.up -side top -pady 4 pack $fud.down -side bottom -pady 4 pack $lf1 -side left -expand yes -fill both pack $flr -side left pack $lf2 -side left -expand yes -fill both pack $fud -side left return $w } proc Swaptablelist::extract_column {colList} { set retList [list] foreach col $colList { set clen [llength $col] switch $clen { 3 { set cWidth [lindex $col 0] set cName [lindex $col 1] set cJust [lindex $col 2] } 2 { set cWidth 0 set cName [lindex $col 0] set cJust [lindex $col 1] } 1 { set cWidth 0 set cName $col set cJust left } default { set cWidth 0 set cName unknown set cJust left } } lappend retList $cWidth $cName $cJust } return $retList } # return the min unless string1 or string2 is longer, if so return # length of the longer one proc Swaptablelist::strmin {min s1 s2} { if {[string length $s1] > $min || [string length $s2] > $min} { return [expr { ([string length $s1] > [string length $s2]) \ ? [string length $s1] \ : [string length $s2] }] } else { return $min } } proc Swaptablelist::parseOpts {var opts input} { upvar $var output for {set i 0} {$i < [llength $input]} {incr i} { for {set a 0} {$a < [llength $opts]} {incr a} { if {[lindex $opts $a 0] == [lindex $input $i]} { break } } if {$a == [llength $opts]} { error "unknown option [lindex $input $i]" } set opt [lindex $opts $a] if {[llength $opt] > 1} { foreach {opt type} $opt {break} if {[incr i] >= [llength $input]} { error "$opt requires an argument" } if {$type != "" && ![string is $type -strict [lindex $input $i]]} { error "$opt requires argument of type $type" } set output($opt) [lindex $input $i] } else { set output($opt) {} } } } #------------------------------------------------------------------------------ proc editStartCmd {tbl row col text} { set w [$tbl editwinpath] return $text } #------------------------------------------------------------------------------ proc editEndCmd {tbl row col text} { return $text } #------------------------------------------------------------------------------ proc Swaptablelist::ShiftR {w lst} { variable lTbl variable rTbl foreach sel [$lTbl($w) curselection] { set rList [list] for {set colNo 0} {$colNo < [$rTbl($w) columncount]} {incr colNo} { set lNdx [$lTbl($w) columnindex [$rTbl($w) columncget $colNo -name]] lappend rList $colNo [$lTbl($w) cellcget $sel,$lNdx -text] } Swaptablelist::insertR $w $rList } } proc Swaptablelist::insertR {w lst} { variable rTbl set tbLen [llength [$rTbl($w) getkeys 0 end]] if {$tbLen == 0} { set nLst [list] foreach {cNum valL} $lst { lappend nLst $valL } $rTbl($w) insert end $nLst } else { set found 0 foreach k [$rTbl($w) getkeys 0 end] { set ndiff 0 foreach {cNum valL} $lst { set valT [$rTbl($w) cellcget $k,$cNum -text] if {$valT ne $valL} { incr ndiff } } if {$ndiff == 0} { set found 1 break } } if {$found == 0} { set nLst [list] foreach {cNum valL} $lst { lappend nLst $valL } $rTbl($w) insert end $nLst } } } proc Swaptablelist::insert {tbl lst} { set tblCols [$tbl columncount] foreach item $lst { if {[llength $item] <= $tblCols} { $tbl insert end $item } else { set nLst [list] foreach it $item { lappend nLst $it } while {[llength $nLst] < $tblCols} { lappend nLst " " } foreach it $nLst { $tbl insert end $it } } } } proc Swaptablelist::ShiftL {w lst} { variable rTbl $rTbl($w) delete [$rTbl($w) curselection] Swaptablelist::renumberWhenIdle $rTbl($w) } proc Swaptablelist::ShiftUD {w dir} { variable rTbl if {[set sel [$rTbl($w) curselection]] == ""} { return } set list {} # delete in reverse order so shifting indexes dont bite us foreach x [Swaptablelist::lreverse $sel] { # make a list in correct order with the items index and contents set list [linsert $list 0 [list $x [$rTbl($w) get $x]]] $rTbl($w) delete $x } if {$dir == "u"} { set n 0 foreach x $list { set i [lindex $x 0] if {[incr i -1] < $n} {set i $n} $rTbl($w) insert $i [lindex $x 1] $rTbl($w) selection set $i incr n } $rTbl($w) see [expr {[lindex $list 0 0] - 1}] } if {$dir == "d"} { set n [$rTbl($w) index end] foreach x $list { set i [lindex $x 0] if {[incr i] > $n} {set i $n} $rTbl($w) insert $i [lindex $x 1] $rTbl($w) selection set $i incr n } $rTbl($w) see $i } } proc Swaptablelist::getLTable {swl} { variable lTbl return $Swaptablelist::lTbl($swl) } proc Swaptablelist::getRTable {swl} { variable rTbl return $Swaptablelist::rTbl($swl) } proc Swaptablelist::renumberWhenIdle tbl { if {[$tbl attrib afterId] eq ""} { $tbl attrib afterId [after idle [list Swaptablelist::renumber $tbl]] } } proc Swaptablelist::renumber {tbl} { $tbl attrib afterId "" set itemList [$tbl get 0 end] set nCol [$tbl columncount] $tbl delete 0 end foreach item $itemList { set rList [list] for {set colNo 0} {$colNo < $nCol} {incr colNo} { lappend rList [lindex $item $colNo] } $tbl insert end $rList } } # return a list in reversed order proc Swaptablelist::lreverse {list} { set new {} foreach x $list {set new [linsert $new 0 $x]} return $new }
if 0 {
Here is some test code
}
######################################################################## # # test ... # ######################################################################## if {$::useTestDemo} { set ::useTile 1 package require Tk package require BWidget package require Swaptablelist 1.0 proc main { } { interface set_Theme xpnative } proc set_Theme { th } { if {$::tcl_version <= 8.4} { tile::setTheme $th } else { ttk::style theme use $th } } proc interface { } { set w . wm withdraw $w wm title $w "Test swapTableList" wm protocol $w WM_DELETE_WINDOW exit set descmenu { "&File" all file 0 { {command "Show Local" {} "Show local list" {} -command show_local} {separator} {command "E&xit" {} "Exit program" {} -command exit} } "&Theme" all theme 0 { {radiobutton "Default" {} "default" {} -command {set_Theme default}} {radiobutton "Classic" {} "classic" {} -command {set_Theme classic}} {radiobutton "Clam" {} "clam" {} -command {set_Theme clam}} {radiobutton "Step" {} "step" {} -command {set_Theme step}} {radiobutton "alt" {} "alt" {} -command {set_Theme alt}} {radiobutton "winnative" {} "Win native" {} -command {set_Theme winnative}} {radiobutton "xpnative" {} "XP native" {} -command {set_Theme xpnative}} } } set mainframe [MainFrame .mainframe \ -menu $descmenu \ -textvariable ::glob_var(status) \ -progressvar ::glob_var(progressvar) \ -progressmax 100 \ -progresstype normal \ -progressfg blue ] $mainframe showstatusbar status $mainframe addindicator -text "version 0.0" set mf [$mainframe getframe] build_swtl $mf pack $mainframe -fill both -expand yes update idletasks set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 - [winfo vrootx $w]}] set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 - [winfo vrooty $w]}] wm geometry $w +$x+$y raise $w wm deiconify $w } proc build_swtl { fr } { set ::glob_var(list,displayed) 0 set ::glob_var(masterList) [list ] set ::glob_var(local) [list ] set ::glob_var(masterList) { {1 MP1 "one"} {2 MP2 "two"} {3 MP3 "three"} {4 MP4 "four"} {5 MP5 "five"} {6 MP6 "six"} {7 MP7 "seven"} {8 MP8 "eight"} {9 MP9 "nine"} {10 MP10 "ten"} } array set ::options { -title "Testing swapTablelist" -llabel "Master List" -rlabel "Local List" -lcolumns { {5 "Index" center } {15 "MissionPoint ID" left } {40 "Description" left } } -rcolumns { {5 "Index" center } {15 "MissionPoint ID" left } } -height 20 -lwidth 0 -rwidth 0 -llistvar ::glob_var(masterList) -rlistvar ::glob_var(localList) } set f2 [frame $fr.f2] set ::swl [eval [list Swaptablelist::create_swap_list $f2] [array get ::options]] pack $f2 set tbl [Swaptablelist::getLTable $::swl] $tbl columnconfigure 0 -name col_index -editable no -sortmode integer $tbl columnconfigure 1 -name col_mpID -editable no -sortmode dictionary $tbl columnconfigure 2 -name col_desc -editable no -sortmode dictionary set tbl [Swaptablelist::getRTable $::swl] $tbl columnconfigure 0 -name col_index -editable no -sortmode integer $tbl columnconfigure 1 -name col_mpID -editable no -sortmode dictionary } proc show_local { } { puts "Local list : " puts $::glob_var(localList) } proc fill { } { # Old code : Not used in the demo any more, but I left # it in "just in case"... if {$::glob_var(list,displayed)} { return } set tbl [Swaptablelist::getLTable $::swl] set mpList { {1 MP1 "one"} {2 MP2 "two"} {3 MP3 "three"} {4 MP4 "four"} {5 MP5 "five"} {6 MP6 "six"} {7 MP7 "seven"} {8 MP8 "eight"} {9 MP9 "nine"} {10 MP10 "ten"} } Swaptablelist::insert $tbl $mpList set ::glob_var(list,displayed) 1 } main
}
AF - 2009-07-28 17:28:51
Looks good, please consider submitting a patch for inclusion into the tcllib swaplist module.