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

Last change on this file since 1742 was 1742, checked in by mmc, 14 years ago

Added preview of outputs to the irappture builder. Fixed a problem
with the file chooser for the "save as" dialog. Added a distinction
between errors and warnings when looking for problems in the tool
definition.

Fixed a few problems in RapptureGUI affecting the scroller and the
way it refreshes itself when the frame within it suddenly shrinks.
Fixed the histogram to avoid errors when the histogram data is
empty. Fixed the analyzer so you can create it without setting
the -notebookpage option. All of these errors showed up in the
irappture builder.

File size: 14.7 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 <Map> \
150                [itcl::code $_dispatcher event -idle !fixframe-inner]
151            bind $_frame.f <Configure> \
152                [itcl::code $_dispatcher event -idle !fixframe-inner]
153            bind $_frame <Configure> \
154                [itcl::code $_dispatcher event -idle !fixframe-outer]
155        }
156        set widget $_frame
157    }
158
159    #
160    # Plug the specified widget into the scrollbars for this widget.
161    #
162    contents ""
163    grid $widget -row 1 -column 1 -sticky nsew
164    $widget configure \
165        -xscrollcommand [itcl::code $this _widget2sbar x] \
166        -yscrollcommand [itcl::code $this _widget2sbar y]
167
168    $itk_component(xsbar) configure -command [list $widget xview]
169    $itk_component(ysbar) configure -command [list $widget yview]
170    set _contents $widget
171
172    if {[string equal "x11" [tk windowingsystem]]} {
173        bind ${widget} <4> { %W yview scroll -5 units }
174        bind ${widget} <5> { %W yview scroll 5 units }
175    } else {
176        bind ${widget} <MouseWheel> {
177            %W yview scroll [expr {- (%D / 120) * 4}] units
178        }
179    }
180    if {$widget == $_frame} {
181        return $_frame.f
182    }
183    return $widget
184}
185
186# ----------------------------------------------------------------------
187# USAGE: _widget2sbar <which> ?args...?
188#
189# Used internally to handle communication from the widget to the
190# scrollbar.  If the scrollbars are in "auto" mode, this provides a
191# hook where we can fix their display.
192# ----------------------------------------------------------------------
193itcl::body Rappture::Scroller::_widget2sbar {which args} {
194    if {$itk_option(-xscrollmode) == "auto"} {
195        _fixsbar x
196    }
197    if {$itk_option(-yscrollmode) == "auto"} {
198        _fixsbar y
199    }
200    eval $itk_component(${which}sbar) set $args
201}
202
203# ----------------------------------------------------------------------
204# USAGE: _fixsbar <which> ?<state>?
205#
206# Used internally to show/hide the scrollbar in the <which> direction,
207# which is either "x" or "y".  If the scrollbar is "on", then it is
208# always displayed.  If "off", never displayed.  And if "auto", then
209# it is displayed if needed for the current view.
210# ----------------------------------------------------------------------
211itcl::body Rappture::Scroller::_fixsbar {which {state ""}} {
212    if {![winfo ismapped $itk_component(hull)]} {
213        #
214        # If we're not on yet screen, bail out!  This keeps bad
215        # numbers (from an empty or partially constructed widget)
216        # from prematurely influencing the scrollbar.
217        #
218        return
219    }
220
221    if {$state == ""} {
222        switch -- [string tolower $itk_option(-${which}scrollmode)] {
223            on - 1 - true - yes  { set state 1 }
224            off - 0 - false - no { set state 0 }
225            auto {
226                set state 0
227                if {$_contents != ""} {
228                    set lims [$_contents ${which}view]
229                    if {[lindex $lims 0] != 0 || [lindex $lims 1] != 1} {
230                        set state 1
231                    }
232                }
233            }
234            default {
235                set state 0
236            }
237        }
238    }
239
240    set row 0
241    set col 0
242    switch -- [string tolower $itk_option(-${which}scrollside)] {
243        top {
244            set row 0
245            set col 1
246        }
247        bottom {
248            set row 2
249            set col 1
250        }
251        left {
252            set row 1
253            set col 0
254        }
255        right {
256            set row 1
257            set col 2
258        }
259        default {
260            set row 0
261            set col 0
262        }
263    }
264
265    # show/hide the scrollbar depending on the desired state
266    switch -- $which {
267        x {
268            if {$state} {
269                if {![_lock active x]} {
270                    # grid $itk_component(xsbar) -row 1 -column 0 -sticky ew
271                    if {$col == 1} {
272                        grid $itk_component(xsbar) -row $row -column $col -sticky ew
273                    }
274                }
275            } else {
276                grid forget $itk_component(xsbar)
277                _lock set x
278            }
279        }
280        y {
281            if {$state} {
282                if {![_lock active y]} {
283                    # grid $itk_component(ysbar) -row 0 -column 1 -sticky ns
284                    if {$row == 1} {
285                        grid $itk_component(ysbar) -row $row -column $col -sticky ns
286                    }
287                }
288            } else {
289                grid forget $itk_component(ysbar)
290                _lock set y
291            }
292        }
293    }
294}
295
296# ----------------------------------------------------------------------
297# USAGE: _fixframe <which>
298#
299# Invoked automatically whenever the canvas representing the "frame"
300# keyword is resized.  Updates the scrolling limits for the canvas
301# to the new size.
302# ----------------------------------------------------------------------
303itcl::body Rappture::Scroller::_fixframe {which} {
304    switch -- $which {
305        inner {
306            set w [winfo reqwidth $_frame.f]
307            set h [winfo reqheight $_frame.f]
308            $_frame configure -scrollregion [list 0 0 $w $h]
309            _fixframe outer
310            _lock reset x
311            _lock reset y
312            $_dispatcher event -idle !fixsize
313        }
314        outer {
315            if {[winfo width $_frame] > [winfo reqwidth $_frame.f]} {
316                $_frame itemconfigure frame -width [winfo width $_frame]
317            } else {
318                $_frame itemconfigure frame -width 0
319            }
320            if {[winfo height $_frame] > [winfo reqheight $_frame.f]} {
321                $_frame itemconfigure frame -height [winfo height $_frame]
322            } else {
323                $_frame itemconfigure frame -height 0
324            }
325        }
326    }
327}
328
329# ----------------------------------------------------------------------
330# USAGE: _fixsize
331#
332# Used internally to update the size options for the widget
333# whenever the -width/-height options change.
334# ----------------------------------------------------------------------
335itcl::body Rappture::Scroller::_fixsize {} {
336    if {$itk_option(-width) == "0" && $itk_option(-height) == "0"} {
337        # for default size, let the frame being controlled set the size
338        grid propagate $itk_component(hull) yes
339        if {$_frame == "$itk_component(hull).ifr"} {
340            set w [winfo reqwidth $_frame.f]
341            set h [winfo reqheight $_frame.f]
342            $_frame configure -width $w -height $h
343        }
344    } else {
345        # for specific size, set the overall size of the widget
346        grid propagate $itk_component(hull) no
347        set w $itk_option(-width); if {$w == "0"} { set w 1i }
348        set h $itk_option(-height); if {$h == "0"} { set h 1i }
349        component hull configure -width $w -height $h
350    }
351}
352
353# ----------------------------------------------------------------------
354# USAGE: _lock set <which>
355# USAGE: _lock reset <which>
356# USAGE: _lock active <which>
357#
358# Used internally to lock out vibrations when the x-scrollbar pops
359# into view.  When the x-scrollbar pops up, it reduces the space
360# available for the widget.  For some widgets (e.g., text widget)
361# this changes the view.  A long line may fall off screen, and the
362# x-scrollbar will no longer be necessary.  If the x-scrollbar just
363# appeared, then its lock is active, signalling that it should stay
364# up.
365# ----------------------------------------------------------------------
366itcl::body Rappture::Scroller::_lock {option which} {
367    switch -- $option {
368        set {
369            set _lock($which) 1
370            after cancel [itcl::code $this _lock reset $which]
371            after 50 [itcl::code $this _lock reset $which]
372        }
373        reset {
374            set _lock($which) 0
375        }
376        active {
377            return $_lock($which)
378        }
379        default {
380            error "bad option \"$option\": should be set, reset, active"
381        }
382    }
383}
384
385# ----------------------------------------------------------------------
386# OPTION: -xscrollmode
387# ----------------------------------------------------------------------
388itcl::configbody Rappture::Scroller::xscrollmode {
389    _fixsbar x
390}
391
392# ----------------------------------------------------------------------
393# OPTION: -yscrollmode
394# ----------------------------------------------------------------------
395itcl::configbody Rappture::Scroller::yscrollmode {
396    _fixsbar y
397}
398
399# ----------------------------------------------------------------------
400# OPTION: -xscrollside
401# ----------------------------------------------------------------------
402itcl::configbody Rappture::Scroller::xscrollside {
403    _fixsbar x
404}
405
406# ----------------------------------------------------------------------
407# OPTION: -yscrollside
408# ----------------------------------------------------------------------
409itcl::configbody Rappture::Scroller::yscrollside {
410    _fixsbar y
411}
412
413# ----------------------------------------------------------------------
414# OPTION: -width
415# ----------------------------------------------------------------------
416itcl::configbody Rappture::Scroller::width {
417    # check for proper value
418    winfo pixels $itk_component(hull) $itk_option(-width)
419
420    $_dispatcher event -idle !fixsize
421}
422
423# ----------------------------------------------------------------------
424# OPTION: -height
425# ----------------------------------------------------------------------
426itcl::configbody Rappture::Scroller::height {
427    # check for proper value
428    winfo pixels $itk_component(hull) $itk_option(-height)
429
430    $_dispatcher event -idle !fixsize
431}
Note: See TracBrowser for help on using the repository browser.