listbox.tcl 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180
  1. # ------------------------------------------------------------------------------
  2. # listbox.tcl
  3. # This file is part of Unifix BWidget Toolkit
  4. # $Id$
  5. # ------------------------------------------------------------------------------
  6. # Index of commands:
  7. # - ListBox::create
  8. # - ListBox::configure
  9. # - ListBox::cget
  10. # - ListBox::insert
  11. # - ListBox::itemconfigure
  12. # - ListBox::itemcget
  13. # - ListBox::bindText
  14. # - ListBox::bindImage
  15. # - ListBox::delete
  16. # - ListBox::move
  17. # - ListBox::reorder
  18. # - ListBox::selection
  19. # - ListBox::exists
  20. # - ListBox::index
  21. # - ListBox::item - deprecated
  22. # - ListBox::items
  23. # - ListBox::see
  24. # - ListBox::edit
  25. # - ListBox::xview
  26. # - ListBox::yview
  27. # - ListBox::_update_edit_size
  28. # - ListBox::_destroy
  29. # - ListBox::_see
  30. # - ListBox::_update_scrollregion
  31. # - ListBox::_draw_item
  32. # - ListBox::_redraw_items
  33. # - ListBox::_redraw_selection
  34. # - ListBox::_redraw_listbox
  35. # - ListBox::_redraw_idle
  36. # - ListBox::_resize
  37. # - ListBox::_init_drag_cmd
  38. # - ListBox::_drop_cmd
  39. # - ListBox::_over_cmd
  40. # - ListBox::_auto_scroll
  41. # - ListBox::_scroll
  42. # ------------------------------------------------------------------------------
  43. namespace eval ListBox {
  44. namespace eval Item {
  45. Widget::declare ListBox::Item {
  46. {-indent Int 0 0 {=0}}
  47. {-text String "" 0}
  48. {-font TkResource "" 0 listbox}
  49. {-image TkResource "" 0 label}
  50. {-window String "" 0}
  51. {-fill TkResource black 0 {listbox -foreground}}
  52. {-data String "" 0}
  53. }
  54. }
  55. Widget::tkinclude ListBox canvas :cmd \
  56. remove {-insertwidth -insertbackground -insertborderwidth -insertofftime \
  57. -insertontime -selectborderwidth -closeenough -confine -scrollregion \
  58. -xscrollincrement -yscrollincrement -width -height} \
  59. initialize {-relief sunken -borderwidth 2 -takefocus 1 \
  60. -highlightthickness 1 -width 200}
  61. Widget::declare ListBox {
  62. {-deltax Int 10 0 {=0 ""}}
  63. {-deltay Int 15 0 {=0 ""}}
  64. {-padx Int 20 0 {=0 ""}}
  65. {-background TkResource "" 0 listbox}
  66. {-selectbackground TkResource "" 0 listbox}
  67. {-selectforeground TkResource "" 0 listbox}
  68. {-width TkResource "" 0 listbox}
  69. {-height TkResource "" 0 listbox}
  70. {-redraw Boolean 1 0}
  71. {-multicolumn Boolean 0 0}
  72. {-dropovermode Flag "wpi" 0 "wpi"}
  73. {-bg Synonym -background}
  74. }
  75. DragSite::include ListBox "LISTBOX_ITEM" 1
  76. DropSite::include ListBox {
  77. LISTBOX_ITEM {copy {} move {}}
  78. }
  79. Widget::addmap ListBox "" :cmd {-deltay -yscrollincrement}
  80. proc ::ListBox { path args } { return [eval ListBox::create $path $args] }
  81. proc use {} {}
  82. variable _edit
  83. }
  84. # ------------------------------------------------------------------------------
  85. # Command ListBox::create
  86. # ------------------------------------------------------------------------------
  87. proc ListBox::create { path args } {
  88. Widget::init ListBox $path $args
  89. variable $path
  90. upvar 0 $path data
  91. # widget informations
  92. set data(nrows) -1
  93. # items informations
  94. set data(items) {}
  95. set data(selitems) {}
  96. # update informations
  97. set data(upd,level) 0
  98. set data(upd,afterid) ""
  99. set data(upd,level) 0
  100. set data(upd,delete) {}
  101. # drag and drop informations
  102. set data(dnd,scroll) ""
  103. set data(dnd,afterid) ""
  104. set data(dnd,item) ""
  105. eval canvas $path [Widget::subcget $path :cmd] \
  106. -width [expr {[Widget::getoption $path -width]*8}] \
  107. -height [expr {[Widget::getoption $path -height]*[Widget::getoption $path -deltay]}] \
  108. -xscrollincrement 8
  109. bind $path <Configure> "ListBox::_resize $path"
  110. bind $path <Destroy> "ListBox::_destroy $path"
  111. DragSite::setdrag $path $path ListBox::_init_drag_cmd [Widget::getoption $path -dragendcmd] 1
  112. DropSite::setdrop $path $path ListBox::_over_cmd ListBox::_drop_cmd 1
  113. rename $path ::$path:cmd
  114. proc ::$path { cmd args } "return \[eval ListBox::\$cmd $path \$args\]"
  115. return $path
  116. }
  117. # ------------------------------------------------------------------------------
  118. # Command ListBox::configure
  119. # ------------------------------------------------------------------------------
  120. proc ListBox::configure { path args } {
  121. set res [Widget::configure $path $args]
  122. set ch1 [expr {[Widget::hasChanged $path -deltay dy] |
  123. [Widget::hasChanged $path -padx val] |
  124. [Widget::hasChanged $path -multicolumn val]}]
  125. set ch2 [expr {[Widget::hasChanged $path -selectbackground val] |
  126. [Widget::hasChanged $path -selectforeground val]}]
  127. set redraw 0
  128. if { [Widget::hasChanged $path -height h] } {
  129. $path:cmd configure -height [expr {$h*$dy}]
  130. set redraw 1
  131. }
  132. if { [Widget::hasChanged $path -width w] } {
  133. $path:cmd configure -width [expr {$w*8}]
  134. set redraw 1
  135. }
  136. if { !$redraw } {
  137. if { $ch1 } {
  138. _redraw_idle $path 2
  139. } elseif { $ch2 } {
  140. _redraw_idle $path 1
  141. }
  142. }
  143. if { [Widget::hasChanged $path -redraw bool] && $bool } {
  144. variable $path
  145. upvar 0 $path data
  146. set lvl $data(upd,level)
  147. set data(upd,level) 0
  148. _redraw_idle $path $lvl
  149. }
  150. set force [Widget::hasChanged $path -dragendcmd dragend]
  151. DragSite::setdrag $path $path ListBox::_init_drag_cmd $dragend $force
  152. DropSite::setdrop $path $path ListBox::_over_cmd ListBox::_drop_cmd
  153. return $res
  154. }
  155. # ------------------------------------------------------------------------------
  156. # Command ListBox::cget
  157. # ------------------------------------------------------------------------------
  158. proc ListBox::cget { path option } {
  159. return [Widget::cget $path $option]
  160. }
  161. # ------------------------------------------------------------------------------
  162. # Command ListBox::insert
  163. # ------------------------------------------------------------------------------
  164. proc ListBox::insert { path index item args } {
  165. variable $path
  166. upvar 0 $path data
  167. if { [lsearch $data(items) $item] != -1 } {
  168. return -code error "item \"$item\" already exists"
  169. }
  170. Widget::init ListBox::Item $path.$item $args
  171. if { ![string compare $index "end"] } {
  172. lappend data(items) $item
  173. } else {
  174. set data(items) [linsert $data(items) $index $item]
  175. }
  176. set data(upd,create,$item) $item
  177. _redraw_idle $path 2
  178. return $item
  179. }
  180. # ------------------------------------------------------------------------------
  181. # Command ListBox::itemconfigure
  182. # ------------------------------------------------------------------------------
  183. proc ListBox::itemconfigure { path item args } {
  184. variable $path
  185. upvar 0 $path data
  186. if { [lsearch $data(items) $item] == -1 } {
  187. return -code error "item \"$item\" does not exist"
  188. }
  189. set oldind [Widget::getoption $path.$item -indent]
  190. set res [Widget::configure $path.$item $args]
  191. set chind [Widget::hasChanged $path.$item -indent indent]
  192. set chw [Widget::hasChanged $path.$item -window win]
  193. set chi [Widget::hasChanged $path.$item -image img]
  194. set cht [Widget::hasChanged $path.$item -text txt]
  195. set chf [Widget::hasChanged $path.$item -font fnt]
  196. set chfg [Widget::hasChanged $path.$item -fill fg]
  197. set idn [$path:cmd find withtag n:$item]
  198. if { $idn == "" } {
  199. # item is not drawn yet
  200. _redraw_idle $path 2
  201. return $res
  202. }
  203. set oldb [$path:cmd bbox $idn]
  204. set coords [$path:cmd coords $idn]
  205. set padx [Widget::getoption $path -padx]
  206. set x0 [expr {[lindex $coords 0]-$padx-$oldind+$indent}]
  207. set y0 [lindex $coords 1]
  208. if { $chw || $chi } {
  209. # -window or -image modified
  210. set idi [$path:cmd find withtag i:$item]
  211. set type [lindex [$path:cmd gettags $idi] 0]
  212. if { [string length $win] } {
  213. if { ![string compare $type "win"] } {
  214. $path:cmd itemconfigure $idi -window $win
  215. } else {
  216. $path:cmd delete $idi
  217. $path:cmd create window $x0 $y0 -window $win -anchor w -tags "win i:$item"
  218. }
  219. } elseif { [string length $img] } {
  220. if { ![string compare $type "img"] } {
  221. $path:cmd itemconfigure $idi -image $img
  222. } else {
  223. $path:cmd delete $idi
  224. $path:cmd create image $x0 $y0 -image $img -anchor w -tags "img i:$item"
  225. }
  226. } else {
  227. $path:cmd delete $idi
  228. }
  229. }
  230. if { $cht || $chf || $chfg } {
  231. # -text or -font modified, or -fill modified
  232. $path:cmd itemconfigure $idn -text $txt -font $fnt -fill $fg
  233. _redraw_idle $path 1
  234. }
  235. if { $chind } {
  236. # -indent modified
  237. $path:cmd coords $idn [expr {$x0+$padx}] $y0
  238. $path:cmd coords i:$item $x0 $y0
  239. _redraw_idle $path 1
  240. }
  241. if { [Widget::getoption $path -multicolumn] && ($cht || $chf || $chind) } {
  242. set bbox [$path:cmd bbox $idn]
  243. if { [lindex $bbox 2] > [lindex $oldb 2] } {
  244. _redraw_idle $path 2
  245. }
  246. }
  247. return $res
  248. }
  249. # ------------------------------------------------------------------------------
  250. # Command ListBox::itemcget
  251. # ------------------------------------------------------------------------------
  252. proc ListBox::itemcget { path item option } {
  253. return [Widget::cget $path.$item $option]
  254. }
  255. # ------------------------------------------------------------------------------
  256. # Command ListBox::bindText
  257. # ------------------------------------------------------------------------------
  258. proc ListBox::bindText { path event script } {
  259. if { $script != "" } {
  260. $path:cmd bind "item" $event \
  261. "$script \[string range \[lindex \[$path:cmd gettags current\] 1\] 2 end\]"
  262. } else {
  263. $path:cmd bind "item" $event {}
  264. }
  265. }
  266. # ------------------------------------------------------------------------------
  267. # Command ListBox::bindImage
  268. # ------------------------------------------------------------------------------
  269. proc ListBox::bindImage { path event script } {
  270. if { $script != "" } {
  271. $path:cmd bind "img" $event \
  272. "$script \[string range \[lindex \[$path:cmd gettags current\] 1\] 2 end\]"
  273. } else {
  274. $path:cmd bind "img" $event {}
  275. }
  276. }
  277. # ------------------------------------------------------------------------------
  278. # Command ListBox::delete
  279. # ------------------------------------------------------------------------------
  280. proc ListBox::delete { path args } {
  281. variable $path
  282. upvar 0 $path data
  283. foreach litems $args {
  284. foreach item $litems {
  285. set idx [lsearch $data(items) $item]
  286. if { $idx != -1 } {
  287. set data(items) [lreplace $data(items) $idx $idx]
  288. Widget::destroy $path.$item
  289. if { [info exists data(upd,create,$item)] } {
  290. unset data(upd,create,$item)
  291. } else {
  292. lappend data(upd,delete) $item
  293. }
  294. }
  295. }
  296. }
  297. set sel $data(selitems)
  298. set data(selitems) {}
  299. eval selection $path set $sel
  300. _redraw_idle $path 2
  301. }
  302. # ------------------------------------------------------------------------------
  303. # Command ListBox::move
  304. # ------------------------------------------------------------------------------
  305. proc ListBox::move { path item index } {
  306. variable $path
  307. upvar 0 $path data
  308. if { [set idx [lsearch $data(items) $item]] == -1 } {
  309. return -code error "item \"$item\" does not exist"
  310. }
  311. set data(items) [lreplace $data(items) $idx $idx]
  312. if { ![string compare $index "end"] } {
  313. lappend data($path,item) $item
  314. } else {
  315. set data(items) [linsert $data(items) $index $item]
  316. }
  317. _redraw_idle $path 2
  318. }
  319. # ------------------------------------------------------------------------------
  320. # Command ListBox::reorder
  321. # ------------------------------------------------------------------------------
  322. proc ListBox::reorder { path neworder } {
  323. variable $path
  324. upvar 0 $path data
  325. set data(items) [BWidget::lreorder $data(items) $neworder]
  326. _redraw_idle $path 2
  327. }
  328. # ------------------------------------------------------------------------------
  329. # Command ListBox::selection
  330. # ------------------------------------------------------------------------------
  331. proc ListBox::selection { path cmd args } {
  332. variable $path
  333. upvar 0 $path data
  334. switch -- $cmd {
  335. set {
  336. set data(selitems) {}
  337. foreach item $args {
  338. if { [lsearch $data(selitems) $item] == -1 } {
  339. if { [lsearch $data(items) $item] != -1 } {
  340. lappend data(selitems) $item
  341. }
  342. }
  343. }
  344. }
  345. add {
  346. foreach item $args {
  347. if { [lsearch $data(selitems) $item] == -1 } {
  348. if { [lsearch $data(items) $item] != -1 } {
  349. lappend data(selitems) $item
  350. }
  351. }
  352. }
  353. }
  354. remove {
  355. foreach item $args {
  356. if { [set idx [lsearch $data(selitems) $item]] != -1 } {
  357. set data(selitems) [lreplace $data(selitems) $idx $idx]
  358. }
  359. }
  360. }
  361. clear {
  362. set data(selitems) {}
  363. }
  364. get {
  365. return $data(selitems)
  366. }
  367. default {
  368. return
  369. }
  370. }
  371. _redraw_idle $path 1
  372. }
  373. # ------------------------------------------------------------------------------
  374. # Command ListBox::exists
  375. # ------------------------------------------------------------------------------
  376. proc ListBox::exists { path item } {
  377. variable $path
  378. upvar 0 $path data
  379. return [expr {[lsearch $data(items) $item] != -1}]
  380. }
  381. # ------------------------------------------------------------------------------
  382. # Command ListBox::index
  383. # ------------------------------------------------------------------------------
  384. proc ListBox::index { path item } {
  385. variable $path
  386. upvar 0 $path data
  387. return [lsearch $data(items) $item]
  388. }
  389. # ------------------------------------------------------------------------------
  390. # Command ListBox::item - deprecated
  391. # ------------------------------------------------------------------------------
  392. proc ListBox::item { path first {last ""} } {
  393. variable $path
  394. upvar 0 $path data
  395. if { ![string length $last] } {
  396. return [lindex $data(items) $first]
  397. } else {
  398. return [lrange $data(items) $first $last]
  399. }
  400. }
  401. # ------------------------------------------------------------------------------
  402. # Command ListBox::items
  403. # ------------------------------------------------------------------------------
  404. proc ListBox::items { path {first ""} {last ""}} {
  405. variable $path
  406. upvar 0 $path data
  407. if { ![string length $first] } {
  408. return $data(items)
  409. }
  410. if { ![string length $last] } {
  411. return [lindex $data(items) $first]
  412. } else {
  413. return [lrange $data(items) $first $last]
  414. }
  415. }
  416. # ------------------------------------------------------------------------------
  417. # Command ListBox::see
  418. # ------------------------------------------------------------------------------
  419. proc ListBox::see { path item } {
  420. variable $path
  421. upvar 0 $path data
  422. if { [Widget::getoption $path -redraw] && $data(upd,afterid) != "" } {
  423. after cancel $data(upd,afterid)
  424. _redraw_listbox $path
  425. }
  426. set idn [$path:cmd find withtag n:$item]
  427. if { $idn != "" } {
  428. ListBox::_see $path $idn right
  429. ListBox::_see $path $idn left
  430. }
  431. }
  432. # ------------------------------------------------------------------------------
  433. # Command ListBox::edit
  434. # ------------------------------------------------------------------------------
  435. proc ListBox::edit { path item text {verifycmd ""} {clickres 0} {select 1}} {
  436. variable _edit
  437. variable $path
  438. upvar 0 $path data
  439. if { [Widget::getoption $path -redraw] && $data(upd,afterid) != "" } {
  440. after cancel $data(upd,afterid)
  441. _redraw_listbox $path
  442. }
  443. set idn [$path:cmd find withtag n:$item]
  444. if { $idn != "" } {
  445. ListBox::_see $path $idn right
  446. ListBox::_see $path $idn left
  447. set oldfg [$path:cmd itemcget $idn -fill]
  448. set sbg [Widget::getoption $path -selectbackground]
  449. set coords [$path:cmd coords $idn]
  450. set x [lindex $coords 0]
  451. set y [lindex $coords 1]
  452. set bd [expr {[$path:cmd cget -borderwidth]+[$path:cmd cget -highlightthickness]}]
  453. set w [expr {[winfo width $path] - 2*$bd}]
  454. set wmax [expr {[$path:cmd canvasx $w]-$x}]
  455. $path:cmd itemconfigure $idn -fill [Widget::getoption $path -background]
  456. $path:cmd itemconfigure s:$item -fill {} -outline {}
  457. set _edit(text) $text
  458. set _edit(wait) 0
  459. set frame [frame $path.edit \
  460. -relief flat -borderwidth 0 -highlightthickness 0 \
  461. -background [Widget::getoption $path -background]]
  462. set ent [entry $frame.edit \
  463. -width 0 \
  464. -relief solid \
  465. -borderwidth 1 \
  466. -highlightthickness 0 \
  467. -foreground [Widget::getoption $path.$item -fill] \
  468. -background [Widget::getoption $path -background] \
  469. -selectforeground [Widget::getoption $path -selectforeground] \
  470. -selectbackground $sbg \
  471. -font [Widget::getoption $path.$item -font] \
  472. -textvariable ListBox::_edit(text)]
  473. pack $ent -ipadx 8 -anchor w
  474. set idw [$path:cmd create window $x $y -window $frame -anchor w]
  475. trace variable ListBox::_edit(text) w "ListBox::_update_edit_size $path $ent $idw $wmax"
  476. tkwait visibility $ent
  477. grab $frame
  478. BWidget::focus set $ent
  479. _update_edit_size $path $ent $idw $wmax
  480. update
  481. if { $select } {
  482. $ent selection range 0 end
  483. $ent icursor end
  484. $ent xview end
  485. }
  486. bind $ent <Escape> {set ListBox::_edit(wait) 0}
  487. bind $ent <Return> {set ListBox::_edit(wait) 1}
  488. if { $clickres == 0 || $clickres == 1 } {
  489. bind $frame <Button> "set ListBox::_edit(wait) $clickres"
  490. }
  491. set ok 0
  492. while { !$ok } {
  493. tkwait variable ListBox::_edit(wait)
  494. if { !$_edit(wait) || $verifycmd == "" ||
  495. [uplevel \#0 $verifycmd [list $_edit(text)]] } {
  496. set ok 1
  497. }
  498. }
  499. trace vdelete ListBox::_edit(text) w "ListBox::_update_edit_size $path $ent $idw $wmax"
  500. grab release $frame
  501. BWidget::focus release $ent
  502. destroy $frame
  503. $path:cmd delete $idw
  504. $path:cmd itemconfigure $idn -fill $oldfg
  505. $path:cmd itemconfigure s:$item -fill $sbg -outline $sbg
  506. if { $_edit(wait) } {
  507. return $_edit(text)
  508. }
  509. }
  510. return ""
  511. }
  512. # ------------------------------------------------------------------------------
  513. # Command ListBox::xview
  514. # ------------------------------------------------------------------------------
  515. proc ListBox::xview { path args } {
  516. return [eval $path:cmd xview $args]
  517. }
  518. # ------------------------------------------------------------------------------
  519. # Command ListBox::yview
  520. # ------------------------------------------------------------------------------
  521. proc ListBox::yview { path args } {
  522. return [eval $path:cmd yview $args]
  523. }
  524. # ------------------------------------------------------------------------------
  525. # Command ListBox::_update_edit_size
  526. # ------------------------------------------------------------------------------
  527. proc ListBox::_update_edit_size { path entry idw wmax args } {
  528. set entw [winfo reqwidth $entry]
  529. if { $entw >= $wmax } {
  530. $path:cmd itemconfigure $idw -width $wmax
  531. } else {
  532. $path:cmd itemconfigure $idw -width 0
  533. }
  534. }
  535. # ------------------------------------------------------------------------------
  536. # Command ListBox::_destroy
  537. # ------------------------------------------------------------------------------
  538. proc ListBox::_destroy { path } {
  539. variable $path
  540. upvar 0 $path data
  541. if { $data(upd,afterid) != "" } {
  542. after cancel $data(upd,afterid)
  543. }
  544. if { $data(dnd,afterid) != "" } {
  545. after cancel $data(dnd,afterid)
  546. }
  547. foreach item $data(items) {
  548. Widget::destroy $path.$item
  549. }
  550. Widget::destroy $path
  551. unset data
  552. rename $path {}
  553. }
  554. # ------------------------------------------------------------------------------
  555. # Command ListBox::_see
  556. # ------------------------------------------------------------------------------
  557. proc ListBox::_see { path idn side } {
  558. set bbox [$path:cmd bbox $idn]
  559. set scrl [$path:cmd cget -scrollregion]
  560. set ymax [lindex $scrl 3]
  561. set dy [$path:cmd cget -yscrollincrement]
  562. set yv [$path:cmd yview]
  563. set yv0 [expr {round([lindex $yv 0]*$ymax/$dy)}]
  564. set yv1 [expr {round([lindex $yv 1]*$ymax/$dy)}]
  565. set y [expr {int([lindex [$path:cmd coords $idn] 1]/$dy)}]
  566. if { $y < $yv0 } {
  567. $path:cmd yview scroll [expr {$y-$yv0}] units
  568. } elseif { $y >= $yv1 } {
  569. $path:cmd yview scroll [expr {$y-$yv1+1}] units
  570. }
  571. set xmax [lindex $scrl 2]
  572. set dx [$path:cmd cget -xscrollincrement]
  573. set xv [$path:cmd xview]
  574. if { ![string compare $side "right"] } {
  575. set xv1 [expr {round([lindex $xv 1]*$xmax/$dx)}]
  576. set x1 [expr {int([lindex $bbox 2]/$dx)}]
  577. if { $x1 >= $xv1 } {
  578. $path:cmd xview scroll [expr {$x1-$xv1+1}] units
  579. }
  580. } else {
  581. set xv0 [expr {round([lindex $xv 0]*$xmax/$dx)}]
  582. set x0 [expr {int([lindex $bbox 0]/$dx)}]
  583. if { $x0 < $xv0 } {
  584. $path:cmd xview scroll [expr {$x0-$xv0}] units
  585. }
  586. }
  587. }
  588. # ------------------------------------------------------------------------------
  589. # Command ListBox::_update_scrollregion
  590. # ------------------------------------------------------------------------------
  591. proc ListBox::_update_scrollregion { path } {
  592. set bd [expr {2*([$path:cmd cget -borderwidth]+[$path:cmd cget -highlightthickness])}]
  593. set w [expr {[winfo width $path] - $bd}]
  594. set h [expr {[winfo height $path] - $bd}]
  595. set xinc [$path:cmd cget -xscrollincrement]
  596. set yinc [$path:cmd cget -yscrollincrement]
  597. set bbox [$path:cmd bbox all]
  598. if { [llength $bbox] } {
  599. set xs [lindex $bbox 2]
  600. set ys [lindex $bbox 3]
  601. if { $w < $xs } {
  602. set w [expr {int($xs)}]
  603. if { [set r [expr {$w % $xinc}]] } {
  604. set w [expr {$w+$xinc-$r}]
  605. }
  606. }
  607. if { $h < $ys } {
  608. set h [expr {int($ys)}]
  609. if { [set r [expr {$h % $yinc}]] } {
  610. set h [expr {$h+$yinc-$r}]
  611. }
  612. }
  613. }
  614. $path:cmd configure -scrollregion [list 0 0 $w $h]
  615. }
  616. # ------------------------------------------------------------------------------
  617. # Command ListBox::_draw_item
  618. # ------------------------------------------------------------------------------
  619. proc ListBox::_draw_item { path item x0 x1 y } {
  620. set indent [Widget::getoption $path.$item -indent]
  621. $path:cmd create text [expr {$x1+$indent}] $y \
  622. -text [Widget::getoption $path.$item -text] \
  623. -fill [Widget::getoption $path.$item -fill] \
  624. -font [Widget::getoption $path.$item -font] \
  625. -anchor w \
  626. -tags "item n:$item"
  627. if { [set win [Widget::getoption $path.$item -window]] != "" } {
  628. $path:cmd create window [expr {$x0+$indent}] $y \
  629. -window $win -anchor w -tags "win i:$item"
  630. } elseif { [set img [Widget::getoption $path.$item -image]] != "" } {
  631. $path:cmd create image [expr {$x0+$indent}] $y \
  632. -image $img -anchor w -tags "img i:$item"
  633. }
  634. }
  635. # ------------------------------------------------------------------------------
  636. # Command ListBox::_redraw_items
  637. # ------------------------------------------------------------------------------
  638. proc ListBox::_redraw_items { path } {
  639. variable $path
  640. upvar 0 $path data
  641. $path:cmd configure -cursor watch
  642. set dx [Widget::getoption $path -deltax]
  643. set dy [Widget::getoption $path -deltay]
  644. set padx [Widget::getoption $path -padx]
  645. set y0 [expr {$dy/2}]
  646. set x0 4
  647. set x1 [expr {$x0+$padx}]
  648. set nitem 0
  649. set drawn {}
  650. set data(xlist) {}
  651. if { [Widget::getoption $path -multicolumn] } {
  652. set nrows $data(nrows)
  653. } else {
  654. set nrows [llength $data(items)]
  655. }
  656. foreach item $data(upd,delete) {
  657. $path:cmd delete i:$item n:$item s:$item
  658. }
  659. foreach item $data(items) {
  660. if { [info exists data(upd,create,$item)] } {
  661. _draw_item $path $item $x0 $x1 $y0
  662. unset data(upd,create,$item)
  663. } else {
  664. set indent [Widget::getoption $path.$item -indent]
  665. $path:cmd coords n:$item [expr {$x1+$indent}] $y0
  666. $path:cmd coords i:$item [expr {$x0+$indent}] $y0
  667. }
  668. incr y0 $dy
  669. incr nitem
  670. lappend drawn n:$item
  671. if { $nitem == $nrows } {
  672. set y0 [expr {$dy/2}]
  673. set bbox [eval $path:cmd bbox $drawn]
  674. set drawn {}
  675. set x0 [expr {[lindex $bbox 2]+$dx}]
  676. set x1 [expr {$x0+$padx}]
  677. set nitem 0
  678. lappend data(xlist) [lindex $bbox 2]
  679. }
  680. }
  681. if { $nitem && $nitem < $nrows } {
  682. set bbox [eval $path:cmd bbox $drawn]
  683. lappend data(xlist) [lindex $bbox 2]
  684. }
  685. set data(upd,delete) {}
  686. $path:cmd configure -cursor [Widget::getoption $path -cursor]
  687. }
  688. # ------------------------------------------------------------------------------
  689. # Command ListBox::_redraw_selection
  690. # ------------------------------------------------------------------------------
  691. proc ListBox::_redraw_selection { path } {
  692. variable $path
  693. upvar 0 $path data
  694. set selbg [Widget::getoption $path -selectbackground]
  695. set selfg [Widget::getoption $path -selectforeground]
  696. foreach id [$path:cmd find withtag sel] {
  697. set item [string range [lindex [$path:cmd gettags $id] 1] 2 end]
  698. $path:cmd itemconfigure "n:$item" -fill [Widget::getoption $path.$item -fill]
  699. }
  700. $path:cmd delete sel
  701. foreach item $data(selitems) {
  702. set bbox [$path:cmd bbox "n:$item"]
  703. if { [llength $bbox] } {
  704. set id [eval $path:cmd create rectangle $bbox -fill $selbg -outline $selbg -tags [list "sel s:$item"]]
  705. $path:cmd itemconfigure "n:$item" -fill $selfg
  706. $path:cmd lower $id
  707. }
  708. }
  709. }
  710. # ------------------------------------------------------------------------------
  711. # Command ListBox::_redraw_listbox
  712. # ------------------------------------------------------------------------------
  713. proc ListBox::_redraw_listbox { path } {
  714. variable $path
  715. upvar 0 $path data
  716. if { [Widget::getoption $path -redraw] } {
  717. if { $data(upd,level) == 2 } {
  718. _redraw_items $path
  719. }
  720. _redraw_selection $path
  721. _update_scrollregion $path
  722. set data(upd,level) 0
  723. set data(upd,afterid) ""
  724. }
  725. }
  726. # ------------------------------------------------------------------------------
  727. # Command ListBox::_redraw_idle
  728. # ------------------------------------------------------------------------------
  729. proc ListBox::_redraw_idle { path level } {
  730. variable $path
  731. upvar 0 $path data
  732. if { $data(nrows) != -1 } {
  733. # widget is realized
  734. if { [Widget::getoption $path -redraw] && $data(upd,afterid) == "" } {
  735. set data(upd,afterid) [after idle ListBox::_redraw_listbox $path]
  736. }
  737. }
  738. if { $level > $data(upd,level) } {
  739. set data(upd,level) $level
  740. }
  741. return ""
  742. }
  743. # ------------------------------------------------------------------------------
  744. # Command ListBox::_resize
  745. # ------------------------------------------------------------------------------
  746. proc ListBox::_resize { path } {
  747. variable $path
  748. upvar 0 $path data
  749. if { [Widget::getoption $path -multicolumn] } {
  750. set bd [expr {[$path:cmd cget -borderwidth]+[$path:cmd cget -highlightthickness]}]
  751. set h [expr {[winfo height $path] - 2*$bd}]
  752. set nrows [expr {$h/[$path:cmd cget -yscrollincrement]}]
  753. if { $nrows == 0 } {
  754. set nrows 1
  755. }
  756. if { $nrows != $data(nrows) } {
  757. set data(nrows) $nrows
  758. _redraw_idle $path 2
  759. } else {
  760. _update_scrollregion $path
  761. }
  762. } elseif { $data(nrows) == -1 } {
  763. # first Configure event
  764. set data(nrows) 0
  765. ListBox::_redraw_listbox $path
  766. } else {
  767. _update_scrollregion $path
  768. }
  769. }
  770. # ------------------------------------------------------------------------------
  771. # Command ListBox::_init_drag_cmd
  772. # ------------------------------------------------------------------------------
  773. proc ListBox::_init_drag_cmd { path X Y top } {
  774. set ltags [$path:cmd gettags current]
  775. set item [lindex $ltags 0]
  776. if { ![string compare $item "item"] ||
  777. ![string compare $item "img"] ||
  778. ![string compare $item "win"] } {
  779. set item [string range [lindex $ltags 1] 2 end]
  780. if { [set cmd [Widget::getoption $path -draginitcmd]] != "" } {
  781. return [uplevel \#0 $cmd [list $path $item $top]]
  782. }
  783. if { [set type [Widget::getoption $path -dragtype]] == "" } {
  784. set type "LISTBOX_ITEM"
  785. }
  786. if { [set img [Widget::getoption $path.$item -image]] != "" } {
  787. pack [label $top.l -image $img -padx 0 -pady 0]
  788. }
  789. return [list $type {copy move link} $item]
  790. }
  791. return {}
  792. }
  793. # ------------------------------------------------------------------------------
  794. # Command ListBox::_drop_cmd
  795. # ------------------------------------------------------------------------------
  796. proc ListBox::_drop_cmd { path source X Y op type dnddata } {
  797. variable $path
  798. upvar 0 $path data
  799. if { [string length $data(dnd,afterid)] } {
  800. after cancel $data(dnd,afterid)
  801. set data(dnd,afterid) ""
  802. }
  803. $path:cmd delete drop
  804. set data(dnd,scroll) ""
  805. if { [llength $data(dnd,item)] } {
  806. if { [set cmd [Widget::getoption $path -dropcmd]] != "" } {
  807. return [uplevel \#0 $cmd [list $path $source $data(dnd,item) $op $type $dnddata]]
  808. }
  809. }
  810. return 0
  811. }
  812. # ------------------------------------------------------------------------------
  813. # Command ListBox::_over_cmd
  814. # ------------------------------------------------------------------------------
  815. proc ListBox::_over_cmd { path source event X Y op type dnddata } {
  816. variable $path
  817. upvar 0 $path data
  818. if { ![string compare $event "leave"] } {
  819. # we leave the window listbox
  820. $path:cmd delete drop
  821. if { [string length $data(dnd,afterid)] } {
  822. after cancel $data(dnd,afterid)
  823. set data(dnd,afterid) ""
  824. }
  825. set data(dnd,scroll) ""
  826. return 0
  827. }
  828. if { ![string compare $event "enter"] } {
  829. # we enter the window listbox - dnd data initialization
  830. set mode [Widget::getoption $path -dropovermode]
  831. set data(dnd,mode) 0
  832. foreach c {w p i} {
  833. set data(dnd,mode) [expr {($data(dnd,mode) << 1) | ([string first $c $mode] != -1)}]
  834. }
  835. }
  836. set x [expr {$X-[winfo rootx $path]}]
  837. set y [expr {$Y-[winfo rooty $path]}]
  838. $path:cmd delete drop
  839. set data(dnd,item) ""
  840. # test for auto-scroll unless mode is widget only
  841. if { $data(dnd,mode) != 4 && [_auto_scroll $path $x $y] != "" } {
  842. return 2
  843. }
  844. if { $data(dnd,mode) & 4 } {
  845. # dropovermode includes widget
  846. set target [list widget]
  847. set vmode 4
  848. } else {
  849. set target [list ""]
  850. set vmode 0
  851. }
  852. if { $data(dnd,mode) & 3 } {
  853. # dropovermode includes item or position
  854. # we extract the box (xi,yi,xs,ys) where we can find item around x,y
  855. set len [llength $data(items)]
  856. set xc [$path:cmd canvasx $x]
  857. set yc [$path:cmd canvasy $y]
  858. set dy [$path:cmd cget -yscrollincrement]
  859. set line [expr {int($yc/$dy)}]
  860. set yi [expr {$line*$dy}]
  861. set ys [expr {$yi+$dy}]
  862. set xi 0
  863. set pos $line
  864. if { [Widget::getoption $path -multicolumn] } {
  865. set nrows $data(nrows)
  866. } else {
  867. set nrows $len
  868. }
  869. if { $line < $nrows } {
  870. foreach xs $data(xlist) {
  871. if { $xc <= $xs } {
  872. break
  873. }
  874. set xi $xs
  875. incr pos $nrows
  876. }
  877. if { $pos < $len } {
  878. set item [lindex $data(items) $pos]
  879. if { $data(dnd,mode) & 1 } {
  880. # dropovermode includes item
  881. lappend target $item
  882. set vmode [expr {$vmode | 1}]
  883. } else {
  884. lappend target ""
  885. }
  886. if { $data(dnd,mode) & 2 } {
  887. # dropovermode includes position
  888. if { $yc >= $yi+$dy/2 } {
  889. # position is after $item
  890. incr pos
  891. set yl $ys
  892. } else {
  893. # position is before $item
  894. set yl $yi
  895. }
  896. lappend target $pos
  897. set vmode [expr {$vmode | 2}]
  898. } else {
  899. lappend target ""
  900. }
  901. } else {
  902. lappend target "" ""
  903. }
  904. } else {
  905. lappend target "" ""
  906. }
  907. if { ($vmode & 3) == 3 } {
  908. # result have both item and position
  909. # we compute what is the preferred method
  910. if { $yc-$yi <= 3 || $ys-$yc <= 3 } {
  911. lappend target "position"
  912. } else {
  913. lappend target "item"
  914. }
  915. }
  916. }
  917. if { $vmode && [set cmd [Widget::getoption $path -dropovercmd]] != "" } {
  918. # user-defined dropover command
  919. set res [uplevel \#0 $cmd [list $source $target $op $type $dnddata]]
  920. set code [lindex $res 0]
  921. set vmode 0
  922. if { $code & 1 } {
  923. # update vmode
  924. set mode [lindex $res 1]
  925. if { ![string compare $mode "item"] } {
  926. set vmode 1
  927. } elseif { ![string compare $mode "position"] } {
  928. set vmode 2
  929. } elseif { ![string compare $mode "widget"] } {
  930. set vmode 4
  931. }
  932. }
  933. } else {
  934. if { ($vmode & 3) == 3 } {
  935. # result have both item and position
  936. # we choose the preferred method
  937. if { ![string compare [lindex $target 3] "position"] } {
  938. set vmode [expr {$vmode & ~1}]
  939. } else {
  940. set vmode [expr {$vmode & ~2}]
  941. }
  942. }
  943. if { $data(dnd,mode) == 4 || $data(dnd,mode) == 0 } {
  944. # dropovermode is widget or empty - recall is not necessary
  945. set code 1
  946. } else {
  947. set code 3
  948. }
  949. }
  950. # draw dnd visual following vmode
  951. if { $vmode & 1 } {
  952. set data(dnd,item) [list "item" [lindex $target 1]]
  953. $path:cmd create rectangle $xi $yi $xs $ys -tags drop
  954. } elseif { $vmode & 2 } {
  955. set data(dnd,item) [concat "position" [lindex $target 2]]
  956. $path:cmd create line $xi $yl $xs $yl -tags drop
  957. } elseif { $vmode & 4 } {
  958. set data(dnd,item) [list "widget"]
  959. } else {
  960. set code [expr {$code & 2}]
  961. }
  962. if { $code & 1 } {
  963. DropSite::setcursor based_arrow_down
  964. } else {
  965. DropSite::setcursor dot
  966. }
  967. return $code
  968. }
  969. # ------------------------------------------------------------------------------
  970. # Command ListBox::_auto_scroll
  971. # ------------------------------------------------------------------------------
  972. proc ListBox::_auto_scroll { path x y } {
  973. variable $path
  974. upvar 0 $path data
  975. set xmax [winfo width $path]
  976. set ymax [winfo height $path]
  977. set scroll {}
  978. if { $y <= 6 } {
  979. if { [lindex [$path:cmd yview] 0] > 0 } {
  980. set scroll [list yview -1]
  981. DropSite::setcursor sb_up_arrow
  982. }
  983. } elseif { $y >= $ymax-6 } {
  984. if { [lindex [$path:cmd yview] 1] < 1 } {
  985. set scroll [list yview 1]
  986. DropSite::setcursor sb_down_arrow
  987. }
  988. } elseif { $x <= 6 } {
  989. if { [lindex [$path:cmd xview] 0] > 0 } {
  990. set scroll [list xview -1]
  991. DropSite::setcursor sb_left_arrow
  992. }
  993. } elseif { $x >= $xmax-6 } {
  994. if { [lindex [$path:cmd xview] 1] < 1 } {
  995. set scroll [list xview 1]
  996. DropSite::setcursor sb_right_arrow
  997. }
  998. }
  999. if { [string length $data(dnd,afterid)] && [string compare $data(dnd,scroll) $scroll] } {
  1000. after cancel $data(dnd,afterid)
  1001. set data(dnd,afterid) ""
  1002. }
  1003. set data(dnd,scroll) $scroll
  1004. if { [llength $scroll] && ![string length $data(dnd,afterid)] } {
  1005. set data(dnd,afterid) [after 200 ListBox::_scroll $path $scroll]
  1006. }
  1007. return $data(dnd,afterid)
  1008. }
  1009. # ------------------------------------------------------------------------------
  1010. # Command ListBox::_scroll
  1011. # ------------------------------------------------------------------------------
  1012. proc ListBox::_scroll { path cmd dir } {
  1013. variable $path
  1014. upvar 0 $path data
  1015. if { ($dir == -1 && [lindex [$path:cmd $cmd] 0] > 0) ||
  1016. ($dir == 1 && [lindex [$path:cmd $cmd] 1] < 1) } {
  1017. $path $cmd scroll $dir units
  1018. set data(dnd,afterid) [after 100 ListBox::_scroll $path $cmd $dir]
  1019. } else {
  1020. set data(dnd,afterid) ""
  1021. DropSite::setcursor dot
  1022. }
  1023. }