source: trunk/gui/scripts/scroller.tcl @ 1536

Last change on this file since 1536 was 1375, checked in by mmc, 15 years ago

Created the SidebarFrame? widget to manage all sidebar options, and plugged
it into the various vis viewers and the xyresult.

File size: 12.0 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: scroller - adds scrollbars to any standard Tk widget
3#
4#  This widget provides automatic scrollbars for any standard Tk
5#  widget.  The scrolled widget should be created as a child of this
6#  widget, and is connected by calling the "contents" method.  Calling
7#  contents with the keyword "frame" creates an internal frame that
8#  allows any collection of widgets to be scrolled.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2005  Purdue Research Foundation
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require Itk
17
18option add *Scroller.xScrollMode off widgetDefault
19option add *Scroller.yScrollMode auto widgetDefault
20option add *Scroller.width 0 widgetDefault
21option add *Scroller.height 0 widgetDefault
22
23itcl::class Rappture::Scroller {
24    inherit itk::Widget
25
26    itk_option define -xscrollmode xScrollMode XScrollMode ""
27    itk_option define -yscrollmode yScrollMode YScrollMode ""
28    itk_option define -width width Width 0
29    itk_option define -height height Height 0
30
31    constructor {args} { # defined below }
32    destructor { # defined below }
33
34    public method contents {{frame "!@#query"}}
35
36    protected method _widget2sbar {which args}
37    protected method _fixsbar {which {state ""}}
38    protected method _fixframe {which}
39    protected method _fixsize {}
40    protected method _lock {option which}
41
42    private variable _dispatcher "" ;# dispatcher for !events
43    private variable _contents ""   ;# widget being controlled
44    private variable _frame ""      ;# for "contents frame" calls
45    private variable _lock          ;# for _lock on x/y scrollbar
46}
47                                                                               
48itk::usual Scroller {
49    keep -background -activebackground -activerelief
50    keep -cursor
51    keep -highlightcolor -highlightthickness
52    keep -troughcolor
53}
54
55# ----------------------------------------------------------------------
56# CONSTRUCTOR
57# ----------------------------------------------------------------------
58itcl::body Rappture::Scroller::constructor {args} {
59    array set _lock { x 0 y 0 }
60
61    Rappture::dispatcher _dispatcher
62
63    $_dispatcher register !fixframe-inner
64    $_dispatcher dispatch $this !fixframe-inner \
65        "[itcl::code $this _fixframe inner]; list"
66
67    $_dispatcher register !fixframe-outer
68    $_dispatcher dispatch $this !fixframe-outer \
69        "[itcl::code $this _fixframe outer]; list"
70
71    $_dispatcher register !fixsize
72    $_dispatcher dispatch $this !fixsize \
73        "[itcl::code $this _fixsize]; list"
74
75    itk_component add xsbar {
76        scrollbar $itk_interior.xsbar -orient horizontal
77    }
78    itk_component add ysbar {
79        scrollbar $itk_interior.ysbar -orient vertical
80    }
81
82    # we don't fix scrollbars when window is withdrawn, so
83    # fix them whenever a window pops up
84    bind $itk_component(hull) <Map> "
85        [itcl::code $this _fixsbar x]
86        [itcl::code $this _fixsbar y]
87    "
88
89    grid rowconfigure $itk_component(hull) 0 -weight 1
90    grid columnconfigure $itk_component(hull) 0 -weight 1
91
92    eval itk_initialize $args
93}
94
95# ----------------------------------------------------------------------
96# DESTRUCTOR
97# ----------------------------------------------------------------------
98itcl::body Rappture::Scroller::destructor {} {
99    after cancel [itcl::code $this _lock reset x]
100    after cancel [itcl::code $this _lock reset y]
101}
102
103# ----------------------------------------------------------------------
104# USAGE: contents ?<widget>|frame?
105#
106# Used to get/set the widget that is being scrolled.  With no args,
107# it returns the name of the widget currently connected to the
108# scrollbars.  Otherwise, the argument specifies a widget to be
109# controlled by the scrollbar.  If the argument is the keyword
110# "frame", then this method creates its own internal frame, which
111# can be packed with other widgets, and returns its name.
112# ----------------------------------------------------------------------
113itcl::body Rappture::Scroller::contents {{widget "!@#query"}} {
114    if {$widget == "!@#query"} {
115        if {$_contents == $_frame} {
116            return $_frame.f
117        }
118        return $_contents
119    }
120
121    #
122    # If the widget is "", then unhook any existing widget.
123    #
124    if {$widget == ""} {
125        if {$_contents != ""} {
126            $_contents configure -xscrollcommand "" -yscrollcommand ""
127            grid forget $_contents
128        }
129        $itk_component(xsbar) configure -command ""
130        $itk_component(ysbar) configure -command ""
131        set _contents ""
132
133        return ""
134    }
135
136    #
137    # For the "frame" keyword, create a canvas that can be scrolled
138    # and return it as the frame being scrolled.
139    #
140    if {$widget == "frame"} {
141        if {$_frame == ""} {
142            set _frame [canvas $itk_component(hull).ifr -highlightthickness 0]
143            frame $_frame.f
144            $_frame create window 0 0 -anchor nw -window $_frame.f -tags frame
145            bind $_frame.f <Configure> \
146                [itcl::code $_dispatcher event -idle !fixframe-inner]
147            bind $_frame <Configure> \
148                [itcl::code $_dispatcher event -idle !fixframe-outer]
149        }
150        set widget $_frame
151    }
152
153    #
154    # Plug the specified widget into the scrollbars for this widget.
155    #
156    contents ""
157    grid $widget -row 0 -column 0 -sticky nsew
158    $widget configure \
159        -xscrollcommand [itcl::code $this _widget2sbar x] \
160        -yscrollcommand [itcl::code $this _widget2sbar y]
161
162    $itk_component(xsbar) configure -command [list $widget xview]
163    $itk_component(ysbar) configure -command [list $widget yview]
164    set _contents $widget
165
166    if {[string equal "x11" [tk windowingsystem]]} {
167        bind ${widget} <4> { %W yview scroll -5 units }
168        bind ${widget} <5> { %W yview scroll 5 units }
169    } else {
170        bind ${widget} <MouseWheel> {
171            %W yview scroll [expr {- (%D / 120) * 4}] units
172        }
173    }
174    if {$widget == $_frame} {
175        return $_frame.f
176    }
177    return $widget
178}
179
180# ----------------------------------------------------------------------
181# USAGE: _widget2sbar <which> ?args...?
182#
183# Used internally to handle communication from the widget to the
184# scrollbar.  If the scrollbars are in "auto" mode, this provides a
185# hook where we can fix their display.
186# ----------------------------------------------------------------------
187itcl::body Rappture::Scroller::_widget2sbar {which args} {
188    if {$itk_option(-xscrollmode) == "auto"} {
189        _fixsbar x
190    }
191    if {$itk_option(-yscrollmode) == "auto"} {
192        _fixsbar y
193    }
194    eval $itk_component(${which}sbar) set $args
195}
196
197# ----------------------------------------------------------------------
198# USAGE: _fixsbar <which> ?<state>?
199#
200# Used internally to show/hide the scrollbar in the <which> direction,
201# which is either "x" or "y".  If the scrollbar is "on", then it is
202# always displayed.  If "off", never displayed.  And if "auto", then
203# it is displayed if needed for the current view.
204# ----------------------------------------------------------------------
205itcl::body Rappture::Scroller::_fixsbar {which {state ""}} {
206    if {![winfo ismapped $itk_component(hull)]} {
207        #
208        # If we're not on yet screen, bail out!  This keeps bad
209        # numbers (from an empty or partially constructed widget)
210        # from prematurely influencing the scrollbar.
211        #
212        return
213    }
214
215    if {$state == ""} {
216        switch -- [string tolower $itk_option(-${which}scrollmode)] {
217            on - 1 - true - yes  { set state 1 }
218            off - 0 - false - no { set state 0 }
219            auto {
220                set state 0
221                if {$_contents != ""} {
222                    set lims [$_contents ${which}view]
223                    if {[lindex $lims 0] != 0 || [lindex $lims 1] != 1} {
224                        set state 1
225                    }
226                }
227            }
228            default {
229                set state 0
230            }
231        }
232    }
233
234    # show/hide the scrollbar depending on the desired state
235    switch -- $which {
236        x {
237            if {$state} {
238                if {![_lock active x]} {
239                    grid $itk_component(xsbar) -row 1 -column 0 -sticky ew
240                }
241            } else {
242                grid forget $itk_component(xsbar)
243                _lock set x
244            }
245        }
246        y {
247            if {$state} {
248                if {![_lock active y]} {
249                    grid $itk_component(ysbar) -row 0 -column 1 -sticky ns
250                }
251            } else {
252                grid forget $itk_component(ysbar)
253                _lock set y
254            }
255        }
256    }
257}
258
259# ----------------------------------------------------------------------
260# USAGE: _fixframe <which>
261#
262# Invoked automatically whenever the canvas representing the "frame"
263# keyword is resized.  Updates the scrolling limits for the canvas
264# to the new size.
265# ----------------------------------------------------------------------
266itcl::body Rappture::Scroller::_fixframe {which} {
267    switch -- $which {
268        inner {
269            $_frame configure -scrollregion [$_frame bbox all]
270            $_dispatcher event -idle !fixsize
271        }
272        outer {
273            if {[winfo width $_frame] > [winfo reqwidth $_frame.f]} {
274                $_frame itemconfigure frame -width [winfo width $_frame]
275            } else {
276                $_frame itemconfigure frame -width 0
277            }
278            if {[winfo height $_frame] > [winfo reqheight $_frame.f]} {
279                $_frame itemconfigure frame -height [winfo height $_frame]
280            } else {
281                $_frame itemconfigure frame -height 0
282            }
283        }
284    }
285}
286
287# ----------------------------------------------------------------------
288# USAGE: _fixsize
289#
290# Used internally to update the size options for the widget
291# whenever the -width/-height options change.
292# ----------------------------------------------------------------------
293itcl::body Rappture::Scroller::_fixsize {} {
294    if {$itk_option(-width) == "0" && $itk_option(-height) == "0"} {
295        # for default size, let the frame being controlled set the size
296        grid propagate $itk_component(hull) yes
297        if {$_frame == "$itk_component(hull).ifr"} {
298            set w [winfo reqwidth $_frame.f]
299            set h [winfo reqheight $_frame.f]
300            $_frame configure -width $w -height $h
301        }
302    } else {
303        # for specific size, set the overall size of the widget
304        grid propagate $itk_component(hull) no
305        set w $itk_option(-width); if {$w == "0"} { set w 1i }
306        set h $itk_option(-height); if {$h == "0"} { set h 1i }
307        component hull configure -width $w -height $h
308    }
309}
310
311# ----------------------------------------------------------------------
312# USAGE: _lock set <which>
313# USAGE: _lock reset <which>
314# USAGE: _lock active <which>
315#
316# Used internally to lock out vibrations when the x-scrollbar pops
317# into view.  When the x-scrollbar pops up, it reduces the space
318# available for the widget.  For some widgets (e.g., text widget)
319# this changes the view.  A long line may fall off screen, and the
320# x-scrollbar will no longer be necessary.  If the x-scrollbar just
321# appeared, then its lock is active, signalling that it should stay
322# up.
323# ----------------------------------------------------------------------
324itcl::body Rappture::Scroller::_lock {option which} {
325    switch -- $option {
326        set {
327            set _lock($which) 1
328            after cancel [itcl::code $this _lock reset $which]
329            after 50 [itcl::code $this _lock reset $which]
330        }
331        reset {
332            set _lock($which) 0
333        }
334        active {
335            return $_lock($which)
336        }
337        default {
338            error "bad option \"$option\": should be set, reset, active"
339        }
340    }
341}
342
343# ----------------------------------------------------------------------
344# OPTION: -xscrollmode
345# ----------------------------------------------------------------------
346itcl::configbody Rappture::Scroller::xscrollmode {
347    _fixsbar x
348}
349
350# ----------------------------------------------------------------------
351# OPTION: -yscrollmode
352# ----------------------------------------------------------------------
353itcl::configbody Rappture::Scroller::yscrollmode {
354    _fixsbar y
355}
356
357# ----------------------------------------------------------------------
358# OPTION: -width
359# ----------------------------------------------------------------------
360itcl::configbody Rappture::Scroller::width {
361    # check for proper value
362    winfo pixels $itk_component(hull) $itk_option(-width)
363
364    $_dispatcher event -idle !fixsize
365}
366
367# ----------------------------------------------------------------------
368# OPTION: -height
369# ----------------------------------------------------------------------
370itcl::configbody Rappture::Scroller::height {
371    # check for proper value
372    winfo pixels $itk_component(hull) $itk_option(-height)
373
374    $_dispatcher event -idle !fixsize
375}
Note: See TracBrowser for help on using the repository browser.