# 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
}

