listbox.tcl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. # listbox.tcl --
  2. #
  3. # This file defines the default bindings for Tk listbox widgets
  4. # and provides procedures that help in implementing those bindings.
  5. #
  6. # Copyright (c) 1994 The Regents of the University of California.
  7. # Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8. # Copyright (c) 1998 by Scriptics Corporation.
  9. #
  10. # See the file "license.terms" for information on usage and redistribution
  11. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12. #--------------------------------------------------------------------------
  13. # tk::Priv elements used in this file:
  14. #
  15. # afterId - Token returned by "after" for autoscanning.
  16. # listboxPrev - The last element to be selected or deselected
  17. # during a selection operation.
  18. # listboxSelection - All of the items that were selected before the
  19. # current selection operation (such as a mouse
  20. # drag) started; used to cancel an operation.
  21. #--------------------------------------------------------------------------
  22. #-------------------------------------------------------------------------
  23. # The code below creates the default class bindings for listboxes.
  24. #-------------------------------------------------------------------------
  25. # Note: the check for existence of %W below is because this binding
  26. # is sometimes invoked after a window has been deleted (e.g. because
  27. # there is a double-click binding on the widget that deletes it). Users
  28. # can put "break"s in their bindings to avoid the error, but this check
  29. # makes that unnecessary.
  30. bind Listbox <1> {
  31. if {[winfo exists %W]} {
  32. tk::ListboxBeginSelect %W [%W index @%x,%y] 1
  33. }
  34. }
  35. # Ignore double clicks so that users can define their own behaviors.
  36. # Among other things, this prevents errors if the user deletes the
  37. # listbox on a double click.
  38. bind Listbox <Double-1> {
  39. # Empty script
  40. }
  41. bind Listbox <B1-Motion> {
  42. set tk::Priv(x) %x
  43. set tk::Priv(y) %y
  44. tk::ListboxMotion %W [%W index @%x,%y]
  45. }
  46. bind Listbox <ButtonRelease-1> {
  47. tk::CancelRepeat
  48. %W activate @%x,%y
  49. }
  50. bind Listbox <Shift-1> {
  51. tk::ListboxBeginExtend %W [%W index @%x,%y]
  52. }
  53. bind Listbox <Control-1> {
  54. tk::ListboxBeginToggle %W [%W index @%x,%y]
  55. }
  56. bind Listbox <B1-Leave> {
  57. set tk::Priv(x) %x
  58. set tk::Priv(y) %y
  59. tk::ListboxAutoScan %W
  60. }
  61. bind Listbox <B1-Enter> {
  62. tk::CancelRepeat
  63. }
  64. bind Listbox <<PrevLine>> {
  65. tk::ListboxUpDown %W -1
  66. }
  67. bind Listbox <<SelectPrevLine>> {
  68. tk::ListboxExtendUpDown %W -1
  69. }
  70. bind Listbox <<NextLine>> {
  71. tk::ListboxUpDown %W 1
  72. }
  73. bind Listbox <<SelectNextLine>> {
  74. tk::ListboxExtendUpDown %W 1
  75. }
  76. bind Listbox <<PrevChar>> {
  77. %W xview scroll -1 units
  78. }
  79. bind Listbox <<PrevWord>> {
  80. %W xview scroll -1 pages
  81. }
  82. bind Listbox <<NextChar>> {
  83. %W xview scroll 1 units
  84. }
  85. bind Listbox <<NextWord>> {
  86. %W xview scroll 1 pages
  87. }
  88. bind Listbox <Prior> {
  89. %W yview scroll -1 pages
  90. %W activate @0,0
  91. }
  92. bind Listbox <Next> {
  93. %W yview scroll 1 pages
  94. %W activate @0,0
  95. }
  96. bind Listbox <Control-Prior> {
  97. %W xview scroll -1 pages
  98. }
  99. bind Listbox <Control-Next> {
  100. %W xview scroll 1 pages
  101. }
  102. bind Listbox <<LineStart>> {
  103. %W xview moveto 0
  104. }
  105. bind Listbox <<LineEnd>> {
  106. %W xview moveto 1
  107. }
  108. bind Listbox <Control-Home> {
  109. %W activate 0
  110. %W see 0
  111. %W selection clear 0 end
  112. %W selection set 0
  113. tk::FireListboxSelectEvent %W
  114. }
  115. bind Listbox <Control-Shift-Home> {
  116. tk::ListboxDataExtend %W 0
  117. }
  118. bind Listbox <Control-End> {
  119. %W activate end
  120. %W see end
  121. %W selection clear 0 end
  122. %W selection set end
  123. tk::FireListboxSelectEvent %W
  124. }
  125. bind Listbox <Control-Shift-End> {
  126. tk::ListboxDataExtend %W [%W index end]
  127. }
  128. bind Listbox <<Copy>> {
  129. if {[selection own -displayof %W] eq "%W"} {
  130. clipboard clear -displayof %W
  131. clipboard append -displayof %W [selection get -displayof %W]
  132. }
  133. }
  134. bind Listbox <space> {
  135. tk::ListboxBeginSelect %W [%W index active]
  136. }
  137. bind Listbox <<Invoke>> {
  138. tk::ListboxBeginSelect %W [%W index active]
  139. }
  140. bind Listbox <Select> {
  141. tk::ListboxBeginSelect %W [%W index active]
  142. }
  143. bind Listbox <Control-Shift-space> {
  144. tk::ListboxBeginExtend %W [%W index active]
  145. }
  146. bind Listbox <Shift-Select> {
  147. tk::ListboxBeginExtend %W [%W index active]
  148. }
  149. bind Listbox <Escape> {
  150. tk::ListboxCancel %W
  151. }
  152. bind Listbox <<SelectAll>> {
  153. tk::ListboxSelectAll %W
  154. }
  155. bind Listbox <<SelectNone>> {
  156. if {[%W cget -selectmode] ne "browse"} {
  157. %W selection clear 0 end
  158. tk::FireListboxSelectEvent %W
  159. }
  160. }
  161. # Additional Tk bindings that aren't part of the Motif look and feel:
  162. bind Listbox <2> {
  163. %W scan mark %x %y
  164. }
  165. bind Listbox <B2-Motion> {
  166. %W scan dragto %x %y
  167. }
  168. # The MouseWheel will typically only fire on Windows and Mac OS X.
  169. # However, someone could use the "event generate" command to produce
  170. # one on other platforms.
  171. if {[tk windowingsystem] eq "aqua"} {
  172. bind Listbox <MouseWheel> {
  173. %W yview scroll [expr {-(%D)}] units
  174. }
  175. bind Listbox <Option-MouseWheel> {
  176. %W yview scroll [expr {-10 * (%D)}] units
  177. }
  178. bind Listbox <Shift-MouseWheel> {
  179. %W xview scroll [expr {-(%D)}] units
  180. }
  181. bind Listbox <Shift-Option-MouseWheel> {
  182. %W xview scroll [expr {-10 * (%D)}] units
  183. }
  184. } else {
  185. bind Listbox <MouseWheel> {
  186. if {%D >= 0} {
  187. %W yview scroll [expr {-%D/30}] units
  188. } else {
  189. %W yview scroll [expr {(29-%D)/30}] units
  190. }
  191. }
  192. bind Listbox <Shift-MouseWheel> {
  193. if {%D >= 0} {
  194. %W xview scroll [expr {-%D/30}] units
  195. } else {
  196. %W xview scroll [expr {(29-%D)/30}] units
  197. }
  198. }
  199. }
  200. if {[tk windowingsystem] eq "x11"} {
  201. # Support for mousewheels on Linux/Unix commonly comes through mapping
  202. # the wheel to the extended buttons. If you have a mousewheel, find
  203. # Linux configuration info at:
  204. # https://linuxreviews.org/HOWTO_change_the_mouse_speed_in_X
  205. bind Listbox <4> {
  206. if {!$tk_strictMotif} {
  207. %W yview scroll -5 units
  208. }
  209. }
  210. bind Listbox <Shift-4> {
  211. if {!$tk_strictMotif} {
  212. %W xview scroll -5 units
  213. }
  214. }
  215. bind Listbox <5> {
  216. if {!$tk_strictMotif} {
  217. %W yview scroll 5 units
  218. }
  219. }
  220. bind Listbox <Shift-5> {
  221. if {!$tk_strictMotif} {
  222. %W xview scroll 5 units
  223. }
  224. }
  225. }
  226. # ::tk::ListboxBeginSelect --
  227. #
  228. # This procedure is typically invoked on button-1 presses. It begins
  229. # the process of making a selection in the listbox. Its exact behavior
  230. # depends on the selection mode currently in effect for the listbox;
  231. # see the Motif documentation for details.
  232. #
  233. # Arguments:
  234. # w - The listbox widget.
  235. # el - The element for the selection operation (typically the
  236. # one under the pointer). Must be in numerical form.
  237. proc ::tk::ListboxBeginSelect {w el {focus 1}} {
  238. variable ::tk::Priv
  239. if {[$w cget -selectmode] eq "multiple"} {
  240. if {[$w selection includes $el]} {
  241. $w selection clear $el
  242. } else {
  243. $w selection set $el
  244. }
  245. } else {
  246. $w selection clear 0 end
  247. $w selection set $el
  248. $w selection anchor $el
  249. set Priv(listboxSelection) {}
  250. set Priv(listboxPrev) $el
  251. }
  252. tk::FireListboxSelectEvent $w
  253. # check existence as ListboxSelect may destroy us
  254. if {$focus && [winfo exists $w] && [$w cget -state] eq "normal"} {
  255. focus $w
  256. }
  257. }
  258. # ::tk::ListboxMotion --
  259. #
  260. # This procedure is called to process mouse motion events while
  261. # button 1 is down. It may move or extend the selection, depending
  262. # on the listbox's selection mode.
  263. #
  264. # Arguments:
  265. # w - The listbox widget.
  266. # el - The element under the pointer (must be a number).
  267. proc ::tk::ListboxMotion {w el} {
  268. variable ::tk::Priv
  269. if {$el == $Priv(listboxPrev)} {
  270. return
  271. }
  272. set anchor [$w index anchor]
  273. switch [$w cget -selectmode] {
  274. browse {
  275. $w selection clear 0 end
  276. $w selection set $el
  277. set Priv(listboxPrev) $el
  278. tk::FireListboxSelectEvent $w
  279. }
  280. extended {
  281. set i $Priv(listboxPrev)
  282. if {$i < 0} {
  283. set i $el
  284. $w selection set $el
  285. }
  286. if {[$w selection includes anchor]} {
  287. $w selection clear $i $el
  288. $w selection set anchor $el
  289. } else {
  290. $w selection clear $i $el
  291. $w selection clear anchor $el
  292. }
  293. if {![info exists Priv(listboxSelection)]} {
  294. set Priv(listboxSelection) [$w curselection]
  295. }
  296. while {($i < $el) && ($i < $anchor)} {
  297. if {$i in $Priv(listboxSelection)} {
  298. $w selection set $i
  299. }
  300. incr i
  301. }
  302. while {($i > $el) && ($i > $anchor)} {
  303. if {$i in $Priv(listboxSelection)} {
  304. $w selection set $i
  305. }
  306. incr i -1
  307. }
  308. set Priv(listboxPrev) $el
  309. tk::FireListboxSelectEvent $w
  310. }
  311. }
  312. }
  313. # ::tk::ListboxBeginExtend --
  314. #
  315. # This procedure is typically invoked on shift-button-1 presses. It
  316. # begins the process of extending a selection in the listbox. Its
  317. # exact behavior depends on the selection mode currently in effect
  318. # for the listbox; see the Motif documentation for details.
  319. #
  320. # Arguments:
  321. # w - The listbox widget.
  322. # el - The element for the selection operation (typically the
  323. # one under the pointer). Must be in numerical form.
  324. proc ::tk::ListboxBeginExtend {w el} {
  325. if {[$w cget -selectmode] eq "extended"} {
  326. if {[$w selection includes anchor]} {
  327. ListboxMotion $w $el
  328. } else {
  329. # No selection yet; simulate the begin-select operation.
  330. ListboxBeginSelect $w $el
  331. }
  332. }
  333. }
  334. # ::tk::ListboxBeginToggle --
  335. #
  336. # This procedure is typically invoked on control-button-1 presses. It
  337. # begins the process of toggling a selection in the listbox. Its
  338. # exact behavior depends on the selection mode currently in effect
  339. # for the listbox; see the Motif documentation for details.
  340. #
  341. # Arguments:
  342. # w - The listbox widget.
  343. # el - The element for the selection operation (typically the
  344. # one under the pointer). Must be in numerical form.
  345. proc ::tk::ListboxBeginToggle {w el} {
  346. variable ::tk::Priv
  347. if {[$w cget -selectmode] eq "extended"} {
  348. set Priv(listboxSelection) [$w curselection]
  349. set Priv(listboxPrev) $el
  350. $w selection anchor $el
  351. if {[$w selection includes $el]} {
  352. $w selection clear $el
  353. } else {
  354. $w selection set $el
  355. }
  356. tk::FireListboxSelectEvent $w
  357. }
  358. }
  359. # ::tk::ListboxAutoScan --
  360. # This procedure is invoked when the mouse leaves an entry window
  361. # with button 1 down. It scrolls the window up, down, left, or
  362. # right, depending on where the mouse left the window, and reschedules
  363. # itself as an "after" command so that the window continues to scroll until
  364. # the mouse moves back into the window or the mouse button is released.
  365. #
  366. # Arguments:
  367. # w - The entry window.
  368. proc ::tk::ListboxAutoScan {w} {
  369. variable ::tk::Priv
  370. if {![winfo exists $w]} return
  371. set x $Priv(x)
  372. set y $Priv(y)
  373. if {$y >= [winfo height $w]} {
  374. $w yview scroll 1 units
  375. } elseif {$y < 0} {
  376. $w yview scroll -1 units
  377. } elseif {$x >= [winfo width $w]} {
  378. $w xview scroll 2 units
  379. } elseif {$x < 0} {
  380. $w xview scroll -2 units
  381. } else {
  382. return
  383. }
  384. ListboxMotion $w [$w index @$x,$y]
  385. set Priv(afterId) [after 50 [list tk::ListboxAutoScan $w]]
  386. }
  387. # ::tk::ListboxUpDown --
  388. #
  389. # Moves the location cursor (active element) up or down by one element,
  390. # and changes the selection if we're in browse or extended selection
  391. # mode.
  392. #
  393. # Arguments:
  394. # w - The listbox widget.
  395. # amount - +1 to move down one item, -1 to move back one item.
  396. proc ::tk::ListboxUpDown {w amount} {
  397. variable ::tk::Priv
  398. $w activate [expr {[$w index active] + $amount}]
  399. $w see active
  400. switch [$w cget -selectmode] {
  401. browse {
  402. $w selection clear 0 end
  403. $w selection set active
  404. tk::FireListboxSelectEvent $w
  405. }
  406. extended {
  407. $w selection clear 0 end
  408. $w selection set active
  409. $w selection anchor active
  410. set Priv(listboxPrev) [$w index active]
  411. set Priv(listboxSelection) {}
  412. tk::FireListboxSelectEvent $w
  413. }
  414. }
  415. }
  416. # ::tk::ListboxExtendUpDown --
  417. #
  418. # Does nothing unless we're in extended selection mode; in this
  419. # case it moves the location cursor (active element) up or down by
  420. # one element, and extends the selection to that point.
  421. #
  422. # Arguments:
  423. # w - The listbox widget.
  424. # amount - +1 to move down one item, -1 to move back one item.
  425. proc ::tk::ListboxExtendUpDown {w amount} {
  426. variable ::tk::Priv
  427. if {[$w cget -selectmode] ne "extended"} {
  428. return
  429. }
  430. set active [$w index active]
  431. if {![info exists Priv(listboxSelection)]} {
  432. $w selection set $active
  433. set Priv(listboxSelection) [$w curselection]
  434. }
  435. $w activate [expr {$active + $amount}]
  436. $w see active
  437. ListboxMotion $w [$w index active]
  438. }
  439. # ::tk::ListboxDataExtend
  440. #
  441. # This procedure is called for key-presses such as Shift-KEndData.
  442. # If the selection mode isn't multiple or extend then it does nothing.
  443. # Otherwise it moves the active element to el and, if we're in
  444. # extended mode, extends the selection to that point.
  445. #
  446. # Arguments:
  447. # w - The listbox widget.
  448. # el - An integer element number.
  449. proc ::tk::ListboxDataExtend {w el} {
  450. set mode [$w cget -selectmode]
  451. if {$mode eq "extended"} {
  452. $w activate $el
  453. $w see $el
  454. if {[$w selection includes anchor]} {
  455. ListboxMotion $w $el
  456. }
  457. } elseif {$mode eq "multiple"} {
  458. $w activate $el
  459. $w see $el
  460. }
  461. }
  462. # ::tk::ListboxCancel
  463. #
  464. # This procedure is invoked to cancel an extended selection in
  465. # progress. If there is an extended selection in progress, it
  466. # restores all of the items between the active one and the anchor
  467. # to their previous selection state.
  468. #
  469. # Arguments:
  470. # w - The listbox widget.
  471. proc ::tk::ListboxCancel w {
  472. variable ::tk::Priv
  473. if {[$w cget -selectmode] ne "extended"} {
  474. return
  475. }
  476. set first [$w index anchor]
  477. set last $Priv(listboxPrev)
  478. if {$last eq ""} {
  479. # Not actually doing any selection right now
  480. return
  481. }
  482. if {$first > $last} {
  483. set tmp $first
  484. set first $last
  485. set last $tmp
  486. }
  487. $w selection clear $first $last
  488. while {$first <= $last} {
  489. if {$first in $Priv(listboxSelection)} {
  490. $w selection set $first
  491. }
  492. incr first
  493. }
  494. tk::FireListboxSelectEvent $w
  495. }
  496. # ::tk::ListboxSelectAll
  497. #
  498. # This procedure is invoked to handle the "select all" operation.
  499. # For single and browse mode, it just selects the active element.
  500. # Otherwise it selects everything in the widget.
  501. #
  502. # Arguments:
  503. # w - The listbox widget.
  504. proc ::tk::ListboxSelectAll w {
  505. set mode [$w cget -selectmode]
  506. if {$mode eq "single" || $mode eq "browse"} {
  507. $w selection clear 0 end
  508. $w selection set active
  509. } else {
  510. $w selection set 0 end
  511. }
  512. tk::FireListboxSelectEvent $w
  513. }
  514. # ::tk::FireListboxSelectEvent
  515. #
  516. # Fire the <<ListboxSelect>> event if the listbox is not in disabled
  517. # state.
  518. #
  519. # Arguments:
  520. # w - The listbox widget.
  521. proc ::tk::FireListboxSelectEvent w {
  522. if {[$w cget -state] eq "normal"} {
  523. event generate $w <<ListboxSelect>>
  524. }
  525. }