1 # vim: foldmarker=<<<,>>>
3 ::itk::usual tlc::Mylistbox {
9 option add *Mylistbox*textBackground [$tlc::theme setting textbackground] widgetDefault
10 option add *Mylistbox*canvasRelief sunken widgetDefault
11 option add *Mylistbox*canvasBorderwidth 2 widgetDefault
12 option add *Mylistbox*labelPos nw widgetDefault
13 option add *Mylistbox.highlightThickness 0 widgetDefault
15 proc tlc::mylistbox {pathName args} {
16 uplevel tlc::Mylistbox [list $pathName] $args
20 class tlc::Mylistbox {
21 inherit tlc::Mywidget tlc::Handlers
25 itk_option define -showdividers showDividers Bool "no" {dividers}
26 itk_option define -dividerwidth dividerWidth Width 1 {dividers}
27 itk_option define -colpad colPad Width 5 {layout}
28 itk_option define -rowpad rowPad Height 0 {layout}
29 itk_option define -colprepad colPrePad Width 3 {layout}
30 itk_option define -rowprepad rowPrePad Width 0 {layout}
31 itk_option define -foreground foreground Foreground \
32 [$tlc::theme setting foreground] {refresh}
33 itk_option define -selectbackground selectBackground Foreground "#186aa0" \
35 itk_option define -selectforeground selectForeground Foreground "#ffc800" \
37 itk_option define -browsecommand browseCommand Command ""
38 itk_option define -doubleclick doubleClick Command ""
39 itk_option define -changedcommand changedCommand Command ""
40 itk_option define -width width Width 80 {newwidth}
41 itk_option define -height height Height 100 {newheight}
42 itk_option define -idcolumn idcolumn Number "" {reset}
43 itk_option define -hidecolumns hideColumns List {} {reset}
44 itk_option define -wrap wrap Wrap 0
45 itk_option define -maxitems maxItems Integer 100
48 method headings {headinglist}
49 method reset_colour {args}
53 method colminsize {col size}
58 method rowcolour {colour rows}
59 method idcolour {colour ids}
60 method columncolour {colour columns}
61 method selection {cmd args}
62 method preprocess_row {row}
64 method insert {index row}
66 method removerow {row}
67 method updaterow {row newrow}
68 method replace {id {newrow {}}}
69 method get {{scope "ids"}}
70 method getrow {type index}
71 method getrows {type index}
73 # Selection manipulation
81 method selected_gate_ref {}
90 variable scroll_lock ""
91 variable scroll_flag 0
93 variable elements_all {}
106 variable oldselected ""
111 variable column_colour
113 variable paged 0; # If configured with > -maxitems
115 variable item_selected
117 method render_row {row elements}
120 method leftclick {x y}
121 method doubleclick {x y}
122 method setselect {row}
123 method browsecallback {row}
124 method setrowcolour {row}
125 method setidcolour {id}
129 method bigger {a1 a2}
130 method smaller {i1 i2}
131 method new_range {newsegment}
135 body tlc::Mylistbox::constructor {args} { #<<<1
136 set hull $itk_interior
138 tlc::Signal #auto item_selected
140 itk_component add frame {
147 itk_component add canvas {
148 canvas $itk_component(frame).canvas \
153 ignore -selectbackground -selectforeground
154 ignore -highlightthickness
155 ignore -width -height
156 rename -background -textbackground textBackground Background
157 rename -borderwidth -canvasborderwidth canvasBorderwidth Borderwidth
158 rename -relief -canvasrelief canvasRelief Relief
160 set canv $itk_component(canvas)
162 itk_component add vsb {
163 myscrollbar_win32 $hull.vsb \
164 -orient v -mode static \
165 -command [list $itk_component(canvas) yview]
168 itk_component add hsb {
169 myscrollbar_win32 $hull.hsb \
170 -orient h -mode dynamic \
171 -command [list $itk_component(canvas) xview]
174 $itk_component(canvas) configure \
175 -yscrollcommand [list $itk_component(vsb) set] \
176 -xscrollcommand [list $itk_component(hsb) set]
178 itk_component add rangeselect {
179 tlc::dropselect $hull.range -menuborderwidth 1 -pady 1
183 grid rowconfigure $hull 3 -weight 1
184 grid columnconfig $hull 1 -weight 1
188 grid $itk_component(rangeselect) -row 1 -column 1 -columnspan 2 \
190 grid remove $itk_component(rangeselect)
191 grid $itk_component(frame) -row 3 -column 1 -sticky news
192 grid rowconfigure $itk_component(frame) 1 -weight 1
193 grid columnconfig $itk_component(frame) 1 -weight 1
195 grid $itk_component(canvas) -row 1 -column 1 -sticky news
196 grid $itk_component(vsb) -row 3 -column 2 -sticky ns
197 grid $itk_component(hsb) -row 4 -column 1 -sticky ew
198 # grid remove $itk_component(vsb)
199 # grid remove $itk_component(hsb)
201 bind $itk_component(canvas) <ButtonRelease-1> [code $this leftclick %x %y]
202 bind $itk_component(canvas) <Double-Button-1> [code $this doubleclick %x %y]
204 $canv yview moveto 0.0
205 eval itk_initialize $args
207 $itk_component(rangeselect) configure -callback [code $this new_range]
211 body tlc::Mylistbox::headings {headings} { #<<<1
215 body tlc::Mylistbox::refresh {} { #<<<1
216 for {set row 0} {$row < $maxrows} {incr row} {
224 body tlc::Mylistbox::reset_colour {args} { #<<<1
225 foreach key [array names row_colour] {
226 unset row_colour($key)
228 foreach key [array names id_colour] {
229 unset id_colour($key)
234 body tlc::Mylistbox::reset {args} { #<<<1
235 set hold [selection get]
236 set old_outer_row [lindex [lsearch $elements_all $hold] 0]
237 if {[llength $args] > 0} {
238 set elements_all [join $args]
241 set totitems [llength $elements_all]
242 if {$totitems > $itk_option(-maxitems)} {
247 lappend choices [list $cur "[expr $cur+1] - [smaller [expr $cur + $itk_option(-maxitems)] $totitems]"]
248 incr items -$itk_option(-maxitems)
249 incr cur $itk_option(-maxitems)
251 $itk_component(rangeselect) configure -choices $choices
252 grid $itk_component(rangeselect)
254 # Swap in the correct page
255 set outer_row [lindex [lsearch $elements_all $hold] 0]
256 if {$outer_row == -1} {
257 set outer_row $old_outer_row
260 set target_page [expr {int($outer_row / $itk_option(-maxitems)) * $itk_option(-maxitems)}]
261 set current_page [$itk_component(rangeselect) get_selected_id]
262 if {$target_page != $current_page} {
263 $itk_component(rangeselect) selectid $target_page
267 # $itk_component(rangeselect) selectid 0
270 grid remove $itk_component(rangeselect)
276 body tlc::Mylistbox::_reset {args} { #<<<1
277 set holdcursor [[winfo toplevel $hull] cget -cursor]
278 [winfo toplevel $hull] configure -cursor watch
280 if {[llength $args] > 0} {
281 set elements [join $args]
284 set hold [selection get]
291 foreach key [array names id2rows] {
298 foreach row $elements {
309 if {$itk_option(-changedcommand) != ""} {
310 uplevel #0 $itk_option(-changedcommand)
313 [winfo toplevel $hull] configure -cursor $holdcursor
317 body tlc::Mylistbox::render_row {row elements} { #<<<1
319 if {$itk_option(-idcolumn) == ""} {
320 set rowid($row) [lindex $elements 0]
323 set rowid($row) [lindex $elements $itk_option(-idcolumn)]
326 lappend id2rows($rowid($row)) $row
327 set visitems [lrange $elements $firstvisitem end]
328 foreach h $itk_option(-hidecolumns) {
331 set cnum [expr $firstvisitem - 1]
332 foreach col $visitems {
333 if {![info exists hidemap([incr cnum])]} {
334 $canv create text 0 0 -text $col \
335 -fill $itk_option(-foreground) \
336 -anchor nw -tags [list "row$row" "col$seq" "rowcol$row,$seq" "elements"]
341 if {$seq > $maxcols} {
347 body tlc::Mylistbox::layout {} { #<<<1
348 foreach item [$canv find withtag "elements"] {
349 $canv coords $item 0 0
352 for {set row 0} {$row < $maxrows} {incr row} {
353 set bbox [$canv bbox "row$row"]
354 set y1 [lindex $bbox 1]
355 set y2 [lindex $bbox 3]
356 set row_size($row) [expr $y2 - $y1 +1]
359 for {set col 0} {$col < $maxcols} {incr col} {
360 set bbox [$canv bbox "col$col"]
361 set x1 [lindex $bbox 0]
362 set x2 [lindex $bbox 2]
363 set col_size($col) [expr $x2 - $x1 +1]
364 if {[info exists col_minsize($col)]} {
365 set col_size($col) [bigger $col_size($col) $col_minsize($col)]
370 for {set row 0} {$row < $maxrows} {incr row} {
371 $canv move "row$row" 0 [expr $row_start($row) + $itk_option(-rowprepad)]
372 set row_start([expr $row+1]) \
373 [expr $row_start($row) + \
375 $itk_option(-rowprepad) + \
376 $itk_option(-rowpad) ]
380 for {set col 0} {$col < $maxcols} {incr col} {
381 $canv move "col$col" [expr $col_start($col) + $itk_option(-colprepad)] 0
382 set col_start([expr $col+1]) \
383 [expr $col_start($col) + \
385 $itk_option(-colprepad) + \
386 $itk_option(-colpad) ]
390 set bbox [$canv bbox elements]
391 $canv configure -scrollregion $bbox
395 body tlc::Mylistbox::dividers {} { #<<<1
396 $canv delete "row_dividers"
398 if {$itk_option(-showdividers)} {
399 for {set div 1} {$div < $maxcols} {incr div} {
400 $canv create line $col_start($div) 0 $col_start($div) 100000 \
401 -tags "row_dividers" \
402 -width $itk_option(-dividerwidth)
408 # use the (very fast) tclx max command if it is available
409 if {[info commands max] == "max"} {
410 body tlc::Mylistbox::bigger {a1 a2} {
414 body tlc::Mylistbox::bigger {a1 a2} {
423 # use the (very fast) tclx max command if it is available
424 if {[info commands min] == "min"} {
425 body tlc::Mylistbox::smaller {i1 i2} {
429 body tlc::Mylistbox::smaller {i1 i2} {
439 body tlc::Mylistbox::new_range {newsegment} { #<<<1
440 puts "got new_range: ($newsegment)"
441 set newbase [lindex $newsegment 0]
442 if {$newbase != ""} {
443 set foo [lrange $elements_all \
445 [expr $newbase + ($itk_option(-maxitems)-1)] \
452 if {[selection getrow] == ""} {
458 body tlc::Mylistbox::colminsize {col size} { #<<<1
459 set col_minsize($col) $size
466 body tlc::Mylistbox::xview {args} { #<<<1
467 eval $canv xview $args
471 body tlc::Mylistbox::yview {args} { #<<<1
472 eval $canv yview $args
476 body tlc::Mylistbox::xset {args} { #<<<1
478 if {[lindex $args 0] == 0 && [lindex $args 1] == 1} {
480 grid remove $itk_component(hsb)
485 grid $itk_component(hsb)
489 eval $itk_component(hsb) set $args
493 body tlc::Mylistbox::yset {args} { #<<<1
495 if {[lindex $args 0] == 0 && [lindex $args 1] == 1} {
497 grid remove $itk_component(vsb)
502 grid $itk_component(vsb)
506 eval $itk_component(vsb) set $args
510 body tlc::Mylistbox::leftclick {x y} { #<<<1
511 set y [$canv canvasy $y]
512 for {set row 1} {$row <= $maxrows} {incr row} {
513 if {$y < $row_start($row)} {
520 if {$row < $maxrows} {
528 # FIXME: doesn't always fire
529 body tlc::Mylistbox::doubleclick {x y} { #<<<1
531 set y [$canv canvasy $y]
532 for {set row 1} {$row <= $maxrows} {incr row} {
533 if {$y < $row_start($row)} {
540 if {$row < $maxrows} {
541 if {$itk_option(-doubleclick) != ""} {
542 if {[info exists rowid($row)]} {
543 uplevel #0 $itk_option(-doubleclick) $rowid($row)
545 uplevel #0 $itk_option(-doubleclick) [list ""]
552 body tlc::Mylistbox::setselect {row} { #<<<1
553 incr [scope scroll_flag]
554 set row [preprocess_row $row]
557 $canv delete "select_highlight"
561 } elseif {$row >= $maxrows} {
566 setrowcolour $oldselected
569 $canv create rectangle \
570 0 $row_start($row) 100000 $row_start([expr $row + 1]) \
571 -outline $itk_option(-selectbackground) \
572 -fill $itk_option(-selectbackground) \
573 -tags "select_highlight"
574 $canv lower "select_highlight" "elements"
575 $canv itemconfigure "row$row" -fill $itk_option(-selectforeground)
576 set oldselected $selected
581 if {$scroll_flag > 1} {
582 if {$scroll_lock != ""} {
583 after cancel $scroll_lock
586 set scroll_lock [after 400 [code $this browsecallback $row]]
596 body tlc::Mylistbox::browsecallback {row} { #<<<1
597 $item_selected set_state [expr {[info exists rowid($row)]}]
598 if {$itk_option(-browsecommand) != ""} {
599 if {[info exists rowid($row)]} {
600 uplevel #0 $itk_option(-browsecommand) [list $rowid($row)]
602 uplevel #0 $itk_option(-browsecommand) [list ""]
606 invoke_handlers onselect \
607 [expr {[info exists rowid($row)] ? $rowid($row) : ""}]
610 incr [scope scroll_flag] -1
614 body tlc::Mylistbox::setrowcolour {row} { #<<<1
617 if {$row == $selected} {
618 set col $itk_option(-selectforeground)
619 } elseif {[info exists row_colour($row)] && $row_colour($row) != ""} {
620 set col $row_colour($row)
621 } elseif {[info exists id_colour($id)] && $id_colour($id) != ""} {
622 set col $id_colour($id)
624 set col $itk_option(-foreground)
627 $canv itemconfigure "row$row" -fill $col
628 if {[array exists column_colour]} {
629 foreach column [array names column_colour] {
630 set colour $column_colour($column)
631 $canv itemconfigure "rowcol$row,$column" -fill $colour
638 body tlc::Mylistbox::setidcolour {id} { #<<<1
640 if {[info exists id2rows($id)]} {
641 foreach row $id2rows($id) {
649 body tlc::Mylistbox::rowcolour {colour rows} { #<<<1
651 set row [preprocess_row $row]
652 set row_colour($row) $colour
658 body tlc::Mylistbox::idcolour {colour ids} { #<<<1
660 set id_colour($id) $colour
666 body tlc::Mylistbox::columncolour {colour columns} { #<<<1
667 foreach column $columns {
668 set column_colour($column) $colour
669 $canv itemconfigure "col$column" -fill $colour
674 body tlc::Mylistbox::newwidth {} { #<<<1
675 if {$itk_option(-width) != ""} {
676 grid columnconfig $hull 1 -minsize $itk_option(-width)
678 grid columnconfig $hull 1 -minsize 0
683 body tlc::Mylistbox::newheight {} { #<<<1
684 if {$itk_option(-height) != ""} {
685 grid rowconfigure $hull 3 -minsize $itk_option(-height)
687 grid rowconfigure $hull 3 -minsize 0
692 body tlc::Mylistbox::selection {cmd args} { #<<<1
695 set id [lindex $args 0]
696 if {[info exists id2rows($id)]} {
697 return [setselect [lindex $id2rows($id) 0]]
699 return [setselect ""]
703 if {$selected != ""} {
704 return [lindex [lindex $elements $selected] 0]
710 return [setselect [lindex $args 0]]
719 body tlc::Mylistbox::preprocess_row {row} { #<<<1
721 "selected" {return [selection getrow]}
722 "last" {return [bigger 0 [expr $maxrows - 1]]}
723 default {return $row}
729 body tlc::Mylistbox::exists {id} { #<<<1
730 return [info exists id2rows($id)]
734 body tlc::Mylistbox::insert {index args} { #<<<1
735 eval set elements_all "\[linsert \$elements_all \$index $args\]"
740 body tlc::Mylistbox::remove {id} { #<<<1
741 if {![info exists id2rows($id)]} return
743 set rows $id2rows($id)
745 set elements_all [lreplace $elements_all $row $row]
751 body tlc::Mylistbox::removerow {row} { #<<<1
752 set row [preprocess_row $row]
754 set elements_all [lreplace $elements_all $row $row]
760 body tlc::Mylistbox::updaterow {row newrow} { #<<<1
766 set row [preprocess_row $row]
769 set elements_all [lreplace $elements_all $row $row $newrow]
775 body tlc::Mylistbox::replace {id {newrow {}}} { #<<<1
776 if {![info exists id2rows($id)]} return
783 if {[selection get] == $id} {
789 set firstrow [lindex $id2rows($id) 0]
790 set elements_all [lreplace $elements_all $firstrow $firstrow $newrow]
792 set otherrows [lrange $id2rows($id) 1 end]
793 foreach row $otherrows {
794 set elements_all [lreplace $elements_all $row $row]
800 selection setrow $firstrow
805 body tlc::Mylistbox::get {{scope "ids"}} { #<<<1
806 switch -exact $scope {
808 return [array names id2rows]
814 error "Unknown scope $scope specified for get, must be one of (ids)"
820 body tlc::Mylistbox::getrow {type index} { #<<<1
821 switch -exact -- $type {
823 if {![info exists id2rows($index)]} {return {}}
824 return [lindex $elements [lindex $id2rows($index) 0]]
827 set index [preprocess_row $index]
829 return [lindex $elements $index]
835 error "bad type: $type. expected one of (-withid, -row)"
841 body tlc::Mylistbox::getrows {type index} { #<<<1
842 switch -exact -- $type {
844 if {![info exists id2rows($index)]} {return {}}
847 foreach row $id2rows($index) {
848 lappend result [lindex $elements $row]
855 set row [preprocess_row $row]
856 lappend result [lindex $elements $row]
861 error "bad type: $type. expected one of (-withid, -rows)"
867 body tlc::Mylistbox::see {row} { #<<<1
868 set row [preprocess_row $row]
869 if {$row == ""} return
871 if {$row == ""} {return}
872 set bbox [$canv bbox "row$row"]
873 set itemtop [lindex $bbox 1]
874 set itembot [lindex $bbox 3]
875 set all [$canv bbox "elements"]
876 if {$all == ""} {return}
877 set height [expr [lindex $all 3] - [lindex $all 1]]
878 set curr [$canv yview]
879 set viewtop [expr $height * [lindex $curr 0]]
880 set viewbot [expr $height * [lindex $curr 1]]
881 set viewheight [expr $viewbot - $viewtop]
882 # puts "viewtop: ($viewtop)\nviewbot: ($viewbot)\nitemtop: ($itemtop)\nitembot: ($itembot)\nviewheight: ($viewheight)\nheight: ($height)\n-------------------------"
884 if {$itemtop < $viewtop || [expr $itembot-$itemtop] > $viewheight} {
885 $canv yview moveto [expr double($itemtop) / $height]
886 } elseif {$itembot > $viewbot} {
887 set viewfactor [expr double($viewheight) / $height]
888 # puts "VIEWFACTOR: ($viewfactor)\nMOVETO: ([expr (double($itembot) / $height) - $viewfactor])\n"
889 $canv yview moveto [expr (double($itembot) / $height) - $viewfactor]
894 body tlc::Mylistbox::next {} { #<<<1
895 if {$selected == ""} {
899 set lsel [expr $selected + 1]
902 if {$lsel >= $maxrows} {
903 if {$itk_option(-wrap)} {
906 set lsel [bigger [expr $maxrows-1] 0]
914 body tlc::Mylistbox::prev {} { #<<<1
915 if {$selected == ""} {
919 set lsel [expr $selected - 1]
922 if {$itk_option(-wrap)} {
923 set lsel [bigger [expr $maxrows-1] 0]
933 body tlc::Mylistbox::home {} { #<<<1
938 body tlc::Mylistbox::end {} { #<<<1
939 set lsel [bigger [expr $maxrows-1] 0]
944 body tlc::Mylistbox::pageup {} { #<<<1
945 $canv yview scroll -1 pages
949 body tlc::Mylistbox::pagedown {} { #<<<1
950 $canv yview scroll +1 pages
954 body tlc::Mylistbox::selected_gate_ref {} { #<<<1
955 return $item_selected