Ticket #14488: listbox.tcl

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