# # Scrolledtext # ---------------------------------------------------------------------- # Implements a scrolled text widget with additional options to manage # the vertical scrollbar. This includes options to control the method # in which the scrollbar is displayed, i.e. statically or dynamically. # Options also exist for adding a label to the scrolled text area and # controlling its position. Import/export of methods are provided for # file I/O. # # ---------------------------------------------------------------------- # AUTHOR: Mark L. Ulferts EMAIL: mulferts@spd.dsccc.com # # @(#) $Id: scrolledtext.itk,v 1.3 2006/09/11 20:35:55 irby Exp $ # ---------------------------------------------------------------------- # Copyright (c) 1995 DSC Technologies Corporation # ====================================================================== # Permission to use, copy, modify, distribute and license this software # and its documentation for any purpose, and without fee or written # agreement with DSC, is hereby granted, provided that the above copyright # notice appears in all copies and that both the copyright notice and # warranty disclaimer below appear in supporting documentation, and that # the names of DSC Technologies Corporation or DSC Communications # Corporation not be used in advertising or publicity pertaining to the # software without specific, written prior permission. # # DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON- # INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE # AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL # DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION, # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. # ====================================================================== # # Default resources. # option add *Scrolledtext.borderWidth 2 widgetDefault option add *Scrolledtext.relief sunken widgetDefault option add *Scrolledtext.scrollMargin 3 widgetDefault option add *Scrolledtext.width 0 widgetDefault option add *Scrolledtext.height 0 widgetDefault option add *Scrolledtext.visibleItems 80x24 widgetDefault option add *Scrolledtext.vscrollMode static widgetDefault option add *Scrolledtext.hscrollMode static widgetDefault option add *Scrolledtext.labelPos n widgetDefault # # Usual options. # itk::usual Scrolledtext { keep -activebackground -activerelief -background -borderwidth -cursor \ -elementborderwidth -foreground -highlightcolor -highlightthickness \ -insertbackground -insertborderwidth -insertofftime -insertontime \ -insertwidth -jump -labelfont -selectbackground -selectborderwidth \ -selectforeground -textbackground -textfont -troughcolor } # ------------------------------------------------------------------ # SCROLLEDTEXT # ------------------------------------------------------------------ class iwidgets::Scrolledtext { inherit iwidgets::Labeledwidget constructor {args} {} destructor {} itk_option define -scrollmargin scrollMargin Margin 3 itk_option define -sbwidth sbWidth Width 15 itk_option define -vscrollmode vscrollMode ScrollMode static itk_option define -hscrollmode hscrollMode ScrollMode static itk_option define -width width Width 0 itk_option define -height height Height 0 itk_option define -visibleitems visibleItems VisibleItems 80x24 public method bbox {index} public method clear {} public method import {filename} public method export {filename} public method compare {index1 op index2} public method debug {args} public method delete {first {last {}}} public method dlineinfo {index} public method get {index1 {index2 {}}} public method index {index} public method insert {args} public method mark {option args} public method scan {option args} public method search {args} public method see {index} public method tag {option args} public method window {option args} public method xview {args} public method yview {args} protected method _scrollText {wid first last} protected method _updateFiller {} protected method _configureFiller {} private method _fillerWidth {} private method _vertScrollbarDisplay {mode} private method _horizScrollbarDisplay {mode} protected variable _vmode off ;# Vertical scroll mode protected variable _hmode off ;# Vertical scroll mode protected variable _reconfigPlanned "" ;# non-null, filler update pending private variable _initialized 0 ;# Initialization flag. } # # Provide a lowercased access method for the Scrolledtext class. # proc ::iwidgets::scrolledtext {pathName args} { uplevel ::iwidgets::Scrolledtext $pathName $args } # ------------------------------------------------------------------ # CONSTRUCTOR # ------------------------------------------------------------------ body iwidgets::Scrolledtext::constructor {args} { component hull configure -borderwidth 0 # # Create some frames to hold both the top and bottom halfs of the # widget. The top will contain both the list and vertical scroll # bar. The bottom houses the horizontal scrollbar and some filler. # itk_component add textframe { frame $itk_interior.textframe } { keep -background -cursor } pack $itk_component(textframe) -fill both -expand yes itk_component add bottomframe { frame $itk_component(textframe).bot } { keep -background -cursor } pack $itk_component(bottomframe) -fill x -side bottom itk_component add hmargin { frame $itk_component(textframe).hm } { keep -background -cursor } pack $itk_component(hmargin) -side bottom itk_component add topframe { frame $itk_component(textframe).top } { keep -background -cursor } pack $itk_component(topframe) -fill both -expand yes # # Frame around text. # itk_component add borderFrame { frame $itk_component(topframe).borderFrame } { keep -background -cursor -borderwidth -relief \ -highlightthickness -highlightcolor rename -highlightbackground -background background Background } # # Create the text area. # itk_component add text { text $itk_component(borderFrame).text \ -borderwidth 0 -relief flat -highlightthickness 0 \ -width 1 -height 1 \ -xscrollcommand \ [code $this _scrollText $itk_component(bottomframe).horizsb] \ -yscrollcommand \ [code $this _scrollText $itk_component(topframe).vertsb] } { keep -cursor -exportselection -foreground \ -insertbackground -insertborderwidth \ -insertofftime -insertontime -insertwidth -padx -pady \ -relief -selectbackground -selectborderwidth \ -selectforeground -setgrid -spacing1 -spacing2 -spacing3 \ -state -wrap rename -font -textfont textFont Font rename -background -textbackground textBackground Background } pack $itk_component(text) -fill both -expand yes # # Create the vertical margin # itk_component add vmargin { frame $itk_component(topframe).vmargin -width 0 } { keep -background -cursor } # # Create the vertical scroll bar. # itk_component add vertsb { scrollbar $itk_component(topframe).vertsb -orient vertical \ -command [code $itk_component(text) yview] } { keep -activebackground -activerelief -background -borderwidth \ -cursor -elementborderwidth \ -highlightcolor -jump -highlightthickness -relief \ -repeatdelay -repeatinterval -troughcolor rename -highlightbackground -background background Background } pack $itk_component(vertsb) -side right -fill y pack $itk_component(vmargin) -side right pack $itk_component(borderFrame) -fill both -expand yes -side left # # Next the horizontal scrollbar. # itk_component add horizsb { scrollbar $itk_component(bottomframe).horizsb -orient horizontal \ -command [code $itk_component(text) xview] } { keep -activebackground -activerelief -background -borderwidth \ -cursor -elementborderwidth \ -highlightcolor -jump -highlightthickness -relief \ -repeatdelay -repeatinterval -troughcolor rename -highlightbackground -background background Background } pack $itk_component(horizsb) -side left -fill x -expand yes # # Create the filler frame and compute its width. # itk_component add filler { frame $itk_component(bottomframe).filler -width [_fillerWidth] } { keep -background -cursor } pack $itk_component(filler) -side right # # Explicitly handle configs that may have been ignored earlier. # eval itk_initialize $args bind $itk_component(hmargin) [code $this _configureFiller] bind $itk_component(vertsb) [code $this _configureFiller] bind $itk_component(vmargin) [code $this _configureFiller] bind $itk_component(horizsb) [code $this _configureFiller] set _initialized 1 configure -scrollmargin $itk_option(-scrollmargin) configure -vscrollmode $itk_option(-vscrollmode) } # ------------------------------------------------------------------ # DESTURCTOR # ------------------------------------------------------------------ body iwidgets::Scrolledtext::destructor {} { if {$_reconfigPlanned != ""} {after cancel $_reconfigPlanned} } # ------------------------------------------------------------------ # OPTIONS # ------------------------------------------------------------------ # ------------------------------------------------------------------ # OPTION: -scrollmargin # Set the distance between the scrollbars and the text area. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::scrollmargin { if {$_initialized} { set pixels [winfo pixels $itk_component(hmargin) \ $itk_option(-scrollmargin)] if {$_hmode == "on"} { $itk_component(hmargin) config -width 1 -height $pixels } if {$_vmode == "on"} { $itk_component(vmargin) config -width $pixels -height 1 } $itk_component(filler) config -width [_fillerWidth] -height 1 } } # ------------------------------------------------------------------ # OPTION: -sbwidth # # Set the width of the scrollbars. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::sbwidth { if {$_initialized} { $itk_component(vertsb) configure -width $itk_option(-sbwidth) $itk_component(horizsb) configure -width $itk_option(-sbwidth) } } # ------------------------------------------------------------------ # OPTION: -vscrollmode # # Enable/disable display and mode of veritcal scrollbar. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::vscrollmode { if {$_initialized} { switch $itk_option(-vscrollmode) { static { _vertScrollbarDisplay on } dynamic - none { _vertScrollbarDisplay off } default { error "bad vscrollmode option\ \"$itk_option(-vscrollmode)\": should\ be static, dynamic, or none" } } } } # ------------------------------------------------------------------ # OPTION: -hscrollmode # # Enable/disable display and mode of horizontal scrollbars. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::hscrollmode { switch $itk_option(-hscrollmode) { static { _horizScrollbarDisplay on } dynamic - none { _horizScrollbarDisplay off } default { error "bad hscrollmode option\ \"$itk_option(-hscrollmode)\": should\ be static, dynamic, or none" } } } # ------------------------------------------------------------------ # OPTION: -width # # Specifies the width of the scrolled text as an entire unit. # The value may be specified in any of the forms acceptable to # Tk_GetPixels. Any additional space needed to display the other # components such as labels, margins, and scrollbars force the text # to be compressed. A value of zero along with the same value for # the height causes the value given for the visibleitems option # to be applied which administers geometry constraints in a different # manner. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::width { if {$itk_option(-width) != 0} { pack propagate $itk_component(shell) no $itk_component(text) configure -width 1 $itk_component(shell) configure \ -width [winfo pixels \ $itk_component(shell) $itk_option(-width)] } else { if {$_initialized} { configure -visibleitems $itk_option(-visibleitems) } } } # ------------------------------------------------------------------ # OPTION: -height # # Specifies the height of the scrolled text as an entire unit. # The value may be specified in any of the forms acceptable to # Tk_GetPixels. Any additional space needed to display the other # components such as labels, margins, and scrollbars force the text # to be compressed. A value of zero along with the same value for # the width causes the value given for the visibleitems option # to be applied which administers geometry constraints in a different # manner. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::height { if {$itk_option(-height) != 0} { pack propagate $itk_component(shell) no $itk_component(text) configure -height 1 $itk_component(shell) configure \ -height [winfo pixels \ $itk_component(shell) $itk_option(-height)] } else { if {$_initialized} { configure -visibleitems $itk_option(-visibleitems) } } } # ------------------------------------------------------------------ # OPTION: -visibleitems # # Specified the widthxheight in characters and lines for the text. # This option is only administered if the width and height options # are both set to zero, otherwise they take precedence. With the # visibleitems option engaged, geometry constraints are maintained # only on the text. The size of the other components such as # labels, margins, and scroll bars, are additive and independent, # effecting the overall size of the scrolled text. In contrast, # should the width and height options have non zero values, they # are applied to the scrolled text as a whole. The text is # compressed or expanded to maintain the geometry constraints. # ------------------------------------------------------------------ configbody iwidgets::Scrolledtext::visibleitems { if {[regexp {^[0-9]+x[0-9]+$} $itk_option(-visibleitems)]} { if {($itk_option(-width) == 0) && \ ($itk_option(-height) == 0)} { set chars [lindex [split $itk_option(-visibleitems) x] 0] set lines [lindex [split $itk_option(-visibleitems) x] 1] pack propagate $itk_component(shell) yes $itk_component(text) configure \ -width $chars -height $lines } } else { error "bad visibleitems option\ \"$itk_option(-visibleitems)\": should be\ widthxheight" } } # ------------------------------------------------------------------ # METHODS # ------------------------------------------------------------------ # ------------------------------------------------------------------ # METHOD: bbox index # # Returns four element list describing the bounding box for the list # item at index # ------------------------------------------------------------------ body iwidgets::Scrolledtext::bbox {index} { return [$itk_component(text) bbox $index] } # ------------------------------------------------------------------ # METHOD clear # # Clear the text area. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::clear {} { $itk_component(text) delete 1.0 end } # ------------------------------------------------------------------ # METHOD import filename # # Load text from an existing file (import filename) # ------------------------------------------------------------------ body iwidgets::Scrolledtext::import {filename} { set f [open $filename r] insert end [read $f] close $f } # ------------------------------------------------------------------ # METHOD export filename # # write text to a file (export filename) # ------------------------------------------------------------------ body iwidgets::Scrolledtext::export {filename} { set f [open $filename w] set txt [$itk_component(text) get 1.0 end] puts $f $txt flush $f close $f } # ------------------------------------------------------------------ # METHOD compare index1 op index2 # # Compare indices according to relational operator. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::compare {index1 op index2} { return [$itk_component(text) compare $index1 $op $index2] } # ------------------------------------------------------------------ # METHOD debug ?boolean? # # Activates consistency checks in B-tree code associated with text # widgets. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::debug {args} { eval $itk_component(text) debug $args } # ------------------------------------------------------------------ # METHOD delete first ?last? # # Delete a range of characters from the text. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::delete {first {last {}}} { $itk_component(text) delete $first $last } # ------------------------------------------------------------------ # METHOD dlineinfo index # # Returns a five element list describing the area occupied by the # display line containing index. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::dlineinfo {index} { return [$itk_component(text) dlineinfo $index] } # ------------------------------------------------------------------ # METHOD get index1 ?index2? # # Return text from start index to end index. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::get {index1 {index2 {}}} { return [$itk_component(text) get $index1 $index2] } # ------------------------------------------------------------------ # METHOD index index # # Return position corresponding to index. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::index {index} { return [$itk_component(text) index $index] } # ------------------------------------------------------------------ # METHOD insert index chars ?tagList? # # Insert text at index. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::insert {args} { eval $itk_component(text) insert $args } # ------------------------------------------------------------------ # METHOD mark option ?arg arg ...? # # Manipulate marks dependent on options. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::mark {option args} { return [eval $itk_component(text) mark $option $args] } # ------------------------------------------------------------------ # METHOD scan option args # # Implements scanning on texts. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::scan {option args} { eval $itk_component(text) scan $option $args } # ------------------------------------------------------------------ # METHOD search ?switches? pattern index ?varName? # # Searches the text for characters matching a pattern. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::search {args} { return [eval $itk_component(text) search $args] } # ------------------------------------------------------------------ # METHOD see index # # Adjusts the view in the window so the character at index is # visible. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::see {index} { $itk_component(text) see $index } # ------------------------------------------------------------------ # METHOD tag option ?arg arg ...? # # Manipulate tags dependent on options. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::tag {option args} { return [eval $itk_component(text) tag $option $args] } # ------------------------------------------------------------------ # METHOD window option ?arg arg ...? # # Manipulate embedded windows. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::window {option args} { return [eval $itk_component(text) window $option $args] } # ------------------------------------------------------------------ # METHOD xview # # Changes x view in widget's window. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::xview {args} { return [eval $itk_component(text) xview $args] } # ------------------------------------------------------------------ # METHOD yview # # Changes y view in widget's window. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::yview {args} { return [eval $itk_component(text) yview $args] } # ------------------------------------------------------------------ # PRIVATE METHOD: _fillerWidth # # Compute the width of the filler frame based on the vertical # scrollbar width plus the margin. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::_fillerWidth {} { if {$_vmode == "on"} { return [expr [winfo reqwidth $itk_component(vertsb)] + \ [winfo reqwidth $itk_component(vmargin)]] } else { return 1 } } # ------------------------------------------------------------------ # PROTECTED METHOD: _configureFiller # # Set up to do an update of the filler if one is not all ready # planned. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::_configureFiller {} { if {$_reconfigPlanned == ""} { set _reconfigPlanned [after idle [code $this _updateFiller]] } } # ------------------------------------------------------------------ # PROTECTED METHOD: _updateFiller # # Update the width of the filler following a configure event on # a scrollbar or margin. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::_updateFiller {} { $itk_component(filler) config -width [_fillerWidth] -height 1 set _reconfigPlanned "" } # ------------------------------------------------------------------ # PRIVATE METHOD: _vertScrollbarDisplay mode # # Displays the vertical scrollbar based on the input mode. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::_vertScrollbarDisplay {mode} { switch $mode { on { set _vmode on $itk_component(vmargin) config -height 1 -width \ [winfo pixels $itk_component(vmargin) \ $itk_option(-scrollmargin)] pack forget $itk_component(borderFrame) pack forget $itk_component(vmargin) pack $itk_component(vertsb) -side right -fill y pack $itk_component(vmargin) -side right pack $itk_component(borderFrame) -fill both -expand yes -side left } off { set _vmode off pack forget $itk_component(vertsb) $itk_component(vmargin) config -width 1 -height 1 $itk_component(filler) config -width 1 -height 1 } default { error "invalid argument \"$mode\": should be on or off" } } } # ------------------------------------------------------------------ # PRIVATE METHOD: _horizScrollbarDisplay mode # # Displays the horizontal scrollbar based on the input mode. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::_horizScrollbarDisplay {mode} { switch $mode { on { set _hmode on $itk_component(hmargin) config -width 1 -height \ [winfo pixels $itk_component(hmargin) \ $itk_option(-scrollmargin)] pack $itk_component(horizsb) -side left -fill x -expand yes } off { set _hmode off pack forget $itk_component(horizsb) $itk_component(hmargin) config -width 1 -height 1 $itk_component(filler) config -width 1 -height 1 } default { error "invalid argument \"$mode\": should be on or off" } } } # ------------------------------------------------------------------ # PRIVATE METHOD: _scrollText wid first last # # Performs scrolling and display of scrollbars based on the first and # last text views as well as the current -vscrollmode and -hscrollmode # settings. # ------------------------------------------------------------------ body iwidgets::Scrolledtext::_scrollText {wid first last} { $wid set $first $last if {$wid == $itk_component(vertsb)} { if {$itk_option(-vscrollmode) == "dynamic"} { if {($first == 0) && ($last == 1)} { if {$_vmode != "off"} { _vertScrollbarDisplay off } } else { if {$_vmode != "on"} { _vertScrollbarDisplay on } } } } elseif {$wid == $itk_component(horizsb)} { if {$itk_option(-hscrollmode) == "dynamic"} { if {($first == 0) && ($last == 1)} { if {$_hmode != "off"} { _horizScrollbarDisplay off } } else { if {$_hmode != "on"} { _horizScrollbarDisplay on } } } } }