source: branches/blt4/gui/scripts/scroller.tcl @ 1642

Last change on this file since 1642 was 1641, checked in by dkearney, 15 years ago

adding support in scroller for selecting the side of the widget you would like the scroll bar to appear on. for y scroll bars, this means you can choose for it to appear on the left or right side. for x scroll bars, this means you can choose for it to appear on the top or bottom.

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