# Shell # ---------------------------------------------------------------------- # This class is implements a shell which is a top level widget # giving a childsite and providing activate, deactivate, and center # methods. # # ---------------------------------------------------------------------- # AUTHOR: Mark L. Ulferts EMAIL: mulferts@austin.dsccc.com # Kris Raney EMAIL: kraney@spd.dsccc.com # # @(#) $Id: shell.itk,v 1.9 2007/06/10 19:35:04 hobbs Exp $ # ---------------------------------------------------------------------- # Copyright (c) 1996 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. # ====================================================================== # # Usual options. # itk::usual Shell { keep -background -cursor -modality } # ------------------------------------------------------------------ # SHELL # ------------------------------------------------------------------ itcl::class iwidgets::Shell { inherit itk::Toplevel constructor {args} {} itk_option define -master master Window "" itk_option define -modality modality Modality none itk_option define -padx padX Pad 0 itk_option define -pady padY Pad 0 itk_option define -width width Width 0 itk_option define -height height Height 0 public method childsite {} public method activate {} public method deactivate {args} public method center {{widget {}}} protected variable _result {} ;# Resultant value for modal activation. private variable _busied {} ;# List of busied top level widgets. common grabstack {} common _wait } # # Provide a lowercased access method for the Shell class. # proc ::iwidgets::shell {pathName args} { uplevel ::iwidgets::Shell $pathName $args } # ------------------------------------------------------------------ # CONSTRUCTOR # ------------------------------------------------------------------ itcl::body iwidgets::Shell::constructor {args} { itk_option add hull.width hull.height # # Maintain a withdrawn state until activated. # wm withdraw $itk_component(hull) # # Create the user child site # itk_component add -protected shellchildsite { frame $itk_interior.shellchildsite } pack $itk_component(shellchildsite) -fill both -expand yes # # Set the itk_interior variable to be the childsite for derived # classes. # set itk_interior $itk_component(shellchildsite) # # Bind the window manager delete protocol to deactivation of the # widget. This can be overridden by the user via the execution # of a similar command outside the class. # wm protocol $itk_component(hull) WM_DELETE_WINDOW [itcl::code $this deactivate] # # Initialize the widget based on the command line options. # eval itk_initialize $args } # ------------------------------------------------------------------ # OPTIONS # ------------------------------------------------------------------ # ------------------------------------------------------------------ # OPTION: -master # # Specifies the master window for the shell. The window manager is # informed that the shell is a transient window whose master is # -masterwindow. # ------------------------------------------------------------------ itcl::configbody iwidgets::Shell::master {} # ------------------------------------------------------------------ # OPTION: -modality # # Specify the modality of the dialog. # ------------------------------------------------------------------ itcl::configbody iwidgets::Shell::modality { switch $itk_option(-modality) { none - application - global { } default { error "bad modality option \"$itk_option(-modality)\":\ should be none, application, or global" } } } # ------------------------------------------------------------------ # OPTION: -padx # # Specifies a padding distance for the childsite in the X-direction. # ------------------------------------------------------------------ itcl::configbody iwidgets::Shell::padx { pack config $itk_component(shellchildsite) -padx $itk_option(-padx) } # ------------------------------------------------------------------ # OPTION: -pady # # Specifies a padding distance for the childsite in the Y-direction. # ------------------------------------------------------------------ itcl::configbody iwidgets::Shell::pady { pack config $itk_component(shellchildsite) -pady $itk_option(-pady) } # ------------------------------------------------------------------ # OPTION: -width # # Specifies the width of the shell. The value may be specified in # any of the forms acceptable to Tk_GetPixels. A value of zero # causes the width to be adjusted to the required value based on # the size requests of the components placed in the childsite. # Otherwise, the width is fixed. # ------------------------------------------------------------------ itcl::configbody iwidgets::Shell::width { # # The width option was added to the hull in the constructor. # So, any width value given is passed automatically to the # hull. All we have to do is play with the propagation. # if {$itk_option(-width) != 0} { pack propagate $itk_component(hull) no } else { pack propagate $itk_component(hull) yes } } # ------------------------------------------------------------------ # OPTION: -height # # Specifies the height of the shell. The value may be specified in # any of the forms acceptable to Tk_GetPixels. A value of zero # causes the height to be adjusted to the required value based on # the size requests of the components placed in the childsite. # Otherwise, the height is fixed. # ------------------------------------------------------------------ itcl::configbody iwidgets::Shell::height { # # The height option was added to the hull in the constructor. # So, any height value given is passed automatically to the # hull. All we have to do is play with the propagation. # if {$itk_option(-height) != 0} { pack propagate $itk_component(hull) no } else { pack propagate $itk_component(hull) yes } } # ------------------------------------------------------------------ # METHODS # ------------------------------------------------------------------ # ------------------------------------------------------------------ # METHOD: childsite # # Return the pathname of the user accessible area. # ------------------------------------------------------------------ itcl::body iwidgets::Shell::childsite {} { return $itk_component(shellchildsite) } # ------------------------------------------------------------------ # METHOD: activate # # Display the dialog and wait based on the modality. For application # and global modal activations, perform a grab operation, and wait # for the result. The result may be returned via an argument to the # "deactivate" method. # ------------------------------------------------------------------ itcl::body iwidgets::Shell::activate {} { if {[winfo ismapped $itk_component(hull)]} { raise $itk_component(hull) return } if {($itk_option(-master) != {}) && \ [winfo exists $itk_option(-master)]} { wm transient $itk_component(hull) $itk_option(-master) } set _wait($this) 0 raise $itk_component(hull) wm deiconify $itk_component(hull) tkwait visibility $itk_component(hull) # For some mysterious reason, Tk sometimes returns too late from the # "tkwait visibility", i.e. after the "deactivate" method was invoked, # i.e. after the dialog window already disappeared. This would lead to # an infinite vwait on _wait($this) further on. Trap this case. # See also 2002-03-15 message to the Tcl/Tk newsgroup. # Remark that tests show that if "raise" is given *after* "deiconify" # (see above), "tkwait visibility" always returns duly on time..... if {![winfo ismapped $itk_component(hull)]} { # means "deactivate" went already through the grab-release stuff. return $_result } # Need to flush the event loop. This line added as a result of # SF ticket #227885. update idletasks if {$itk_option(-modality) == "application"} { if {$grabstack != {}} { grab release [lindex $grabstack end] } set err 1 while {$err == 1} { set err [catch [list grab $itk_component(hull)]] if {$err == 1} { after 1000 } } lappend grabstack [list grab $itk_component(hull)] tkwait variable [itcl::scope _wait($this)] return $_result } elseif {$itk_option(-modality) == "global" } { if {$grabstack != {}} { grab release [lindex $grabstack end] } set err 1 while {$err == 1} { set err [catch [list grab -global $itk_component(hull)]] if {$err == 1} { after 1000 } } lappend grabstack [list grab -global $itk_component(hull)] tkwait variable [itcl::scope _wait($this)] return $_result } } # ------------------------------------------------------------------ # METHOD: deactivate # # Deactivate the display of the dialog. The method takes an optional # argument to passed to the "activate" method which returns the value. # This is only effective for application and global modal dialogs. # ------------------------------------------------------------------ itcl::body iwidgets::Shell::deactivate {args} { if {! [winfo ismapped $itk_component(hull)]} { return } if {$itk_option(-modality) == "none"} { wm withdraw $itk_component(hull) } elseif {$itk_option(-modality) == "application"} { grab release $itk_component(hull) if {$grabstack != {}} { if {[set grabstack [lreplace $grabstack end end]] != {}} { eval [lindex $grabstack end] } } wm withdraw $itk_component(hull) } elseif {$itk_option(-modality) == "global"} { grab release $itk_component(hull) if {$grabstack != {}} { if {[set grabstack [lreplace $grabstack end end]] != {}} { eval [lindex $grabstack end] } } wm withdraw $itk_component(hull) } if {[llength $args]} { set _result $args } else { set _result {} } set _wait($this) 1 return } # ------------------------------------------------------------------ # METHOD: center # # Centers the dialog with respect to another widget or the screen # as a whole. # ------------------------------------------------------------------ itcl::body iwidgets::Shell::center {{widget {}}} { update idletasks set hull $itk_component(hull) set w [winfo width $hull] set h [winfo height $hull] set sh [winfo screenheight $hull] ;# display screen's height/width set sw [winfo screenwidth $hull] # # User can request it centered with respect to root by passing in '{}' # if { $widget == "" } { set reqX [expr {($sw-$w)/2}] set reqY [expr {($sh-$h)/2}] } else { set wfudge 5 ;# wm width fudge factor set hfudge 20 ;# wm height fudge factor set widgetW [winfo width $widget] set widgetH [winfo height $widget] set reqX [expr {[winfo rootx $widget]+($widgetW-($widgetW/2))-($w/2)}] set reqY [expr {[winfo rooty $widget]+($widgetH-($widgetH/2))-($h/2)}] # # Adjust for errors - if too long or too tall # if { ($reqX+$w+$wfudge) > $sw } { set reqX [expr {$sw-$w-$wfudge}] } if { $reqX < $wfudge } { set reqX $wfudge } if { ($reqY+$h+$hfudge) > $sh } { set reqY [expr {$sh-$h-$hfudge}] } if { $reqY < $hfudge } { set reqY $hfudge } } wm geometry $hull +$reqX+$reqY }