cdesktopenv/cde/programs/dtksh/DtFuncs.sh.src

752 lines
23 KiB
Plaintext

# $XConsortium: DtFuncs.sh.src /main/1 1995/11/01 15:49:09 rswiston $
#
# COMPONENT_NAME: desktop
#
# FUNCTIONS: DtkshAddButtons
# DtkshFloatBottom
# DtkshFloatLeft
# DtkshFloatRight
# DtkshFloatTop
# DtkshLeftOf
# DtkshOver
# DtkshRightOf
# DtkshSetReturnKeyControls
# DtkshUnder
#
# ORIGINS: 27,118,119,120,121
#
# This module contains IBM CONFIDENTIAL code. -- (IBM
# Confidential Restricted when combined with the aggregated
# modules for this product)
# OBJECT CODE ONLY SOURCE MATERIALS
#
# (C) COPYRIGHT International Business Machines Corp. 1995
# All Rights Reserved
# US Government Users Restricted Rights - Use, duplication or
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
#
###############################################################################
# (c) Copyright 1993, 1994 Hewlett-Packard Company
# (c) Copyright 1993, 1994 International Business Machines Corp.
# (c) Copyright 1993, 1994 Sun Microsystems, Inc.
# (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
# Novell, Inc.
###############################################################################
###############################################################################
#
# DtkshAddButtons - Convenience function for adding 1 or more buttons of the
# same kind into a composite widget. Most frequently
# used to add a collection of buttons into a menupane.
#
# Usages:
#
# DtkshAddButtons parent widgetClass label1 callback1 [label2 callback2 ...]
#
# DtkshAddButtons [-w] parent widgetClass variable1 label1 callback1 \
# [variable2 label2 callback2 ...]
#
# The "-w" option indicates that the convenience function should return
# the widget handle for each of the created buttons. The widget handle
# is returned in the specified environment variable.
#
# The widgetClass can be one of the following, and will default to the
# XmPushButtonGadget class, if not specified:
#
# XmPushButton
# XmPushButtonGadget
# XmToggleButton
# XmToggleButtonGadget
# XmCascadeButton
# XmCascadeButtonGadget
#
# Examples:
#
# DtkshAddButtons $MENU XmPushButtonGadget Open do_Open Save do_Save Quit exit
#
# DtkshAddButtons -w $MENU XmPushButtonGadget B1 Open do_Open B2 Save do_Save
#
DtkshAddButtons()
{
typeset parent widgetClass callback returnWidget="false" TMP=""
typeset -i paramCount=2
if [ $# -ge 1 ] && [ x"$1" = "x-w" ]; then
returnWidget=true
paramCount=3
shift
fi
if [ $# -lt 2 ]; then
return 1
fi
parent=$1
shift
widgetClass=${1:-XmPushButtonGadget}
shift
case $widgetClass in
XmPushButtonGadget) callback=activateCallback;;
XmPushButton) callback=activateCallback;;
XmToggleButtonGadget) callback=valueChangedCallback;;
XmToggleButton) callback=valueChangedCallback;;
XmCascadeButtonGadget) callback=activateCallback;;
XmCascadeButton) callback=activateCallback;;
*) return 1
esac
while [ $# -ge $paramCount ]
do
if [ "$returnWidget" = true ]; then
if [ ! "$3" = "" ]; then
XtCreateManagedWidget "$1" "$1" $widgetClass "$parent" \
labelString:"$2" ${callback}:"$3"
else
XtCreateManagedWidget "$1" "$1" $widgetClass "$parent" \
labelString:"$2"
fi
shift 3
else
if [ ! "$2" = "" ]; then
XtCreateManagedWidget Id "btn" $widgetClass "$parent" \
labelString:"$1" ${callback}:"$2"
else
XtCreateManagedWidget Id "btn" $widgetClass "$parent" \
labelString:"$1"
fi
shift 2
fi
done
return 0
}
###############################################################################
#
# DtkshSetReturnKeyControls - Convenience function for configuring a text
# widget (within a form!) so that the Return key does not
# activate the default button within the form, but instead
# moves the focus to the next text widget within the form.
# This is useful if you have a window which contains a
# series of text fields, and the default button should not
# be activated until the user presses the Return key in the
# last text field.
#
# Usage:
#
# DtkshSetReturnKeyControls textWidgetId nextTextWidgetId formWidgetId \
# defaultButtonId
#
# The textWidgetId parameter specifies the widget which is to be configured
# to catch the 'Return' key, and force the focus to move to the next text
# widget (as indicated by the nextTextWidgetId parameter). The formWidgetId
# parameter specifies the form which contains the default button, and should
# be the parent of the two text widgets. The defaultButtonId indicates which
# component is to be treated as the default button within the form.
#
# Examples:
#
# DtkshSetReturnKeyControls $TEXT1 $TEXT2 $FORM $OK
# DtkshSetReturnKeyControls $TEXT2 $TEXT3 $FORM $OK
#
DtkshSetReturnKeyControls()
{
if [ $# -ne 4 ]; then
return 1
fi
XtAddCallback $1 focusCallback "XtSetValues $3 defaultButton:NULL"
XtAddCallback $1 losingFocusCallback "XtSetValues $3 defaultButton:$4"
XtOverrideTranslations $1 \
"Ctrl<Key>Return:ksh_eval(\"XmProcessTraversal $2 TRAVERSE_CURRENT\")
<Key>Return:ksh_eval(\"XmProcessTraversal $2 TRAVERSE_CURRENT\")"
return 0
}
###############################################################################
#
# DtkshUnder
# DtkshOver
# DtkshRightOf
# DtkshLeftOf - Convenience functions for specifying form constraints.
# This set of functions allow a component to be attached
# to one of the edges of another component.
#
# Usages:
#
# DtkshUnder widgetId [offset]
# DtkshOver widgetId [offset]
# DtkshRightOf widgetId [offset]
# DtkshLeftOf widgetId [offset]
#
# The widgetId parameter specifies the widget to which the current
# component is to be attached. The offset value is optional, and
# defaults to 0 if not specified.
#
# Examples:
#
# XtCreateManagedWidget BUTTON2 button2 XmPushButton $FORM \
# labelString:"Exit" \
# $(DtkshUnder $BUTTON1)
#
DtkshUnder()
{
if [ $# -lt 1 ]; then
return 1
fi
echo "topWidget:$1 topAttachment:ATTACH_WIDGET topOffset:${2:-0}"
}
DtkshOver()
{
if [ $# -lt 1 ]; then
return 1
fi
echo "bottomWidget:$1 bottomAttachment:ATTACH_WIDGET bottomOffset:${2:-0}"
}
DtkshRightOf()
{
if [ $# -lt 1 ]; then
return 1
fi
echo "leftWidget:$1 leftAttachment:ATTACH_WIDGET leftOffset:${2:-0}"
}
DtkshLeftOf()
{
if [ $# -lt 1 ]; then
return 1
fi
echo "rightWidget:$1 rightAttachment:ATTACH_WIDGET rightOffset:${2:-0}"
}
###############################################################################
#
# DtkshFloatRight
# DtkshFloatLeft
# DtkshFloatTop
# DtkshFloatBottom - Convenience functions for specifying form constraints.
# This set of functions allow a component to be positioned
# independent of the other components within the form.
# As the form grows or shrinks, the component maintains
# its relative position within the form. The component
# may still grow or shrink, depending upon the other form
# constraints which have been specified for the component.
#
# Usages:
#
# DtkshFloatRight [position]
# DtkshFloatLeft [position]
# DtkshFloatTop [position]
# DtkshFloatBottom [position]
#
# The optional position parameter specifies the relative position
# to which the indicated edge of the component will be positioned.
# A default position is used, if not specified.
#
# Examples:
#
# XtCreateManagedWidget BUTTON1 button1 XmPushButton $FORM \
# labelString:"Ok" \
# $(DtkshUnder $SEPARATOR) \
# $(DtkshFloatLeft 10) \
# $(DtkshFloatRight 40)
#
DtkshFloatRight()
{
echo "rightAttachment:ATTACH_POSITION rightPosition:${1:-0}"
}
DtkshFloatLeft()
{
echo "leftAttachment:ATTACH_POSITION leftPosition:${1:-0}"
}
DtkshFloatTop()
{
echo "topAttachment:ATTACH_POSITION topPosition:${1:-0}"
}
DtkshFloatBottom()
{
echo "bottomAttachment:ATTACH_POSITION bottomPosition:${1:-0}"
}
###############################################################################
#
# DtkshAnchorRight
# DtkshAnchorLeft
# DtkshAnchorTop
# DtkshAnchorBottom - Convenience functions for specifying form constraints.
# This set of functions allow a component to be attached
# to one of the edges of the form in such a fashion that
# as the form grows or shrinks, the component's position
# does not change. However, depending upon the other
# form constaints set on this component, the component
# may still grow or shrink in size.
#
# Usages:
#
# DtkshAnchorRight [offset]
# DtkshAnchorLeft [offset]
# DtkshAnchorTop [offset]
# DtkshAnchorBottom [offset]
#
# The optional offset parameter specifies how far from the edge
# of the form the component should be positioned. If an offset
# is not specified, then 0 is user.
#
# Examples:
#
# XtCreateManagedWidget BUTTON1 button1 XmPushButton $FORM \
# labelString:"Ok" \
# $(DtkshUnder $SEPARATOR) \
# $(DtkshAnchorLeft 10) \
# $(DtkshAnchorBottom 10)
#
DtkshAnchorRight()
{
echo "rightAttachment:ATTACH_FORM rightOffset:${1:-0}"
}
DtkshAnchorLeft()
{
echo "leftAttachment:ATTACH_FORM leftOffset:${1:-0}"
}
DtkshAnchorTop()
{
echo "topAttachment:ATTACH_FORM topOffset:${1:-0}"
}
DtkshAnchorBottom()
{
echo "bottomAttachment:ATTACH_FORM bottomOffset:${1:-0}"
}
###############################################################################
#
# DtkshSpanWidth
# DtkshSpanHeight - Convenience functions for specifying form constraints.
# This set of functions allow a component to be configured
# such that it spans either the full height or width of
# the form widget. This effect is accomplished by attaching
# two edges of the component (top & bottom for DtkshSpanHeight,
# and left and right for DtkshSpanWidth) to the form. The
# component will typically resize whenever the form is
# resized.
#
# Usages:
#
# DtkshSpanWidth [offset]
# DtkshSpanHeight [offset]
#
# The optional offset parameter specifies how far from the edge
# of the form the component should be positioned. If an offset
# is not specified, then 0 is user.
#
# Examples:
#
# XtCreateManagedWidget SEPARATOR $FORM XmSeparator \
# $(DtkshSpanWidth 1 1)
#
DtkshSpanWidth()
{
echo "leftAttachment:ATTACH_FORM leftOffset:${1:-0} \
rightAttachment:ATTACH_FORM rightOffset:${2:-0}"
}
DtkshSpanHeight()
{
echo "topAttachment:ATTACH_FORM topOffset:${1:-0} \
bottomAttachment:ATTACH_FORM bottomOffset:${2:-0}"
}
###############################################################################
#
# DtkshDisplayInformationDialog
# DtkshDisplayQuestionDialog
# DtkshDisplayWarningDialog
# DtkshDisplayWorkingDialog
# DtkshDisplayErrorDialog - Convenience functions for creating a single
# instance of each of the flavors of the Motif
# feedback dialog. If an instance of the requested
# type of dialog already exists, then it will be
# reused. The parent of the dialog is obtained
# from the environment variable $TOPLEVEL, which
# should be set by the calling shell script. The
# handle for the requested dialog is returned in
# one of the following environment variables:
#
# _DT_ERROR_DIALOG_HANDLE
# _DT_QUESTION_DIALOG_HANDLE
# _DT_WORKING_DIALOG_HANDLE
# _DT_WARNING_DIALOG_HANDLE
# _DT_INFORMATION_DIALOG_HANDLE
#
# WARNING: IF ATTACHING YOUR OWN CALLBACKS TO THE DIALOG
# BUTTONS, DO NOT DESTROY THE DIALOG WHEN YOU
# ARE DONE WITH IT; SIMPLY UNMANAGE THE DIALOG,
# SO THAT IT CAN BE USED AT A LATER TIME.
#
# Usages:
#
# DtDisplay*Dialog title message okCallback closeCallback helpCallback \
# dialogStyle
#
# The "Ok" button is always managed, and by default will simply unmanage
# the dialog. The "Cancel" and "Help" buttons are only managed when a
# callback is supplied for them.
#
# The "dialogStyle" parameter accepts any of the standard resource settings
# supported by the bulletin board widget.
#
# Examples:
#
# DtkshDisplayErrorDialog "Read Error" "Unable to read the file" \
# "OkCallback" "CancelCallback" "" \
# DIALOG_PRIMARY_APPLICATION_MODAL
#
# Global feedback dialog handles
_DT_ERROR_DIALOG_HANDLE=""
_DT_QUESTION_DIALOG_HANDLE=""
_DT_WORKING_DIALOG_HANDLE=""
_DT_WARNING_DIALOG_HANDLE=""
_DT_INFORMATION_DIALOG_HANDLE=""
_DT_TMP_DIALOG_HANDLE=""
DtkshDisplayErrorDialog()
{
_DtDisplayFeedbackDialog "$_DT_ERROR_DIALOG_HANDLE" "Error" "${@:-}"
if [ "$_DT_ERROR_DIALOG_HANDLE" = "" ] ; then
_DT_ERROR_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
return 0
}
DtkshDisplayQuestionDialog()
{
_DtDisplayFeedbackDialog "$_DT_QUESTION_DIALOG_HANDLE" "Question" "${@:-}"
if [ "$_DT_QUESTION_DIALOG_HANDLE" = "" ] ; then
_DT_QUESTION_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
return 0
}
DtkshDisplayWorkingDialog()
{
_DtDisplayFeedbackDialog "$_DT_WORKING_DIALOG_HANDLE" "Working" "${@:-}"
if [ "$_DT_WORKING_DIALOG_HANDLE" = "" ] ; then
_DT_WORKING_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
return 0
}
DtkshDisplayWarningDialog()
{
_DtDisplayFeedbackDialog "$_DT_WARNING_DIALOG_HANDLE" "Warning" "${@:-}"
if [ "$_DT_WARNING_DIALOG_HANDLE" = "" ] ; then
_DT_WARNING_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
return 0
}
DtkshDisplayInformationDialog()
{
_DtDisplayFeedbackDialog "$_DT_INFORMATION_DIALOG_HANDLE" "Information" \
"${@:-}"
if [ "$_DT_INFORMATION_DIALOG_HANDLE" = "" ] ; then
_DT_INFORMATION_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
return 0
}
###############################################################################
#
# DtkshDisplayQuickHelpDialog
# DtkshDisplayHelpDialog - Convenience functions for creating a single
# instance of a help dialog and a quick help
# dialog. If an instance of the requested type
# of help dialog already exists, then it will be
# reused. The parent of the dialog is obtained
# from the environment variable $TOPLEVEL, which
# should be set by the calling shell script. The
# handle for the requested dialog is returned in
# one of the following environment variables:
#
# _DT_HELP_DIALOG_HANDLE
# _DT_QUICK_HELP_DIALOG_HANDLE
#
# WARNING: DO NOT DESTROY THIS DIALOG, UNLESS YOU ALSO CLEAR THE
# CORRESPONDING ENVIRONMENT VARIABLE, SO THAT THIS CODE
# WILL NOT ATTEMPT TO REUSE THE DIALOG AGAIN.
#
# Usages:
#
# DtDisplay*HelpDialog title helpType helpInformation [locationId]
#
# The meaning of the parameters is dependent upon the value specified
# for the 'helpType' parameter. There meanings are explained below:
#
# helpType = HELP_TYPE_TOPIC
# helpInformation = help volume name
# locationId = help topic location id
#
# helpType = HELP_TYPE_STRING
# helpInformation = help string
# locationId = <not used>
#
# helpType = HELP_TYPE_DYNAMIC_STRING
# helpInformation = help string
# locationId = <not used>
#
# helpType = HELP_TYPE_MAN_PAGE
# helpInformation = man page name
# locationId = <not used>
#
# helpType = HELP_TYPE_FILE
# helpInformation = help file name
# locationId = <not used>
#
# Examples:
#
# DtkshDisplayHelpDialog "Help On Dtksh" HELP_TYPE_FILE "HelpFileName"
#
# Global help dialog handles
_DT_HELP_DIALOG_HANDLE=""
_DT_QUICK_HELP_DIALOG_HANDLE=""
DtkshDisplayQuickHelpDialog()
{
_DtkshDisplayHelpDialog "$_DT_QUICK_HELP_DIALOG_HANDLE" "Quick" "${@:-}"
if [ "$_DT_QUICK_HELP_DIALOG_HANDLE" = "" ] ; then
_DT_QUICK_HELP_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
}
DtkshDisplayHelpDialog()
{
_DtkshDisplayHelpDialog "$_DT_HELP_DIALOG_HANDLE" "" "${@:-}"
if [ "$_DT_HELP_DIALOG_HANDLE" = "" ] ; then
_DT_HELP_DIALOG_HANDLE=$_DT_TMP_DIALOG_HANDLE
fi
}
##############################################################################
#
# This internal shell function performs most of the work required to
# create an instance of a feedback dialog (error, warning, information,
# working and question). It will reuse an existing instance of the
# requested type of feedback dialog, if one has already been created;
# otherwise, it will create a new one.
#
# The "Ok" button is always managed, and by default will simply unpost
# the dialog. The "Cancel" and "Help" buttons are only managed if the
# callers specifies a callback for the butttons. Both the "Ok" and
# "Cancel" buttons rely on the fact that the 'autoUnpost' resource for
# the dialog is 'True'.
#
# The implied parent of the dialog is identified by the environment
# variable '$TOPLEVEL'.
#
# The incoming parameters are defined as follows (note that $1 and $2 are
# defined by the convenience function which is calling us, while $3 - $8
# are the parameters which were passed by the caller to the convenience
# function:
#
# $1 = existing dialog handle, or "" if first time
# $2 = type of feedback dialog (Information, Question, Working, ... )
# $3 = dialog title
# $4 = message string
# $5 = okCallback
# $6 = cancelCallback
# $7 = helpCallback
# $8 = dialogStyle
#
_DtDisplayFeedbackDialog()
{
if [ "$1" = "" ]; then
XmCreate${2}Dialog _DT_TMP_DIALOG_HANDLE $TOPLEVEL "$2"
else
_DT_TMP_DIALOG_HANDLE=$1
fi
XtSetValues $_DT_TMP_DIALOG_HANDLE \
dialogTitle:"${3:-$2}" \
messageString:"${4:- }" \
dialogStyle:"${8:-DIALOG_MODELESS}"
if [ $# -ge 5 ] && [ "$5" != "" ]; then
XtSetValues $_DT_TMP_DIALOG_HANDLE okCallback:"$5"
fi
if [ $# -lt 6 ] || [ "$6" = "" ]; then
XtUnmanageChild $(XmMessageBoxGetChild "-" $_DT_TMP_DIALOG_HANDLE \
DIALOG_CANCEL_BUTTON)
else
XtSetValues $_DT_TMP_DIALOG_HANDLE cancelCallback:"$6"
fi
if [ $# -lt 7 ] || [ "$7" = "" ]; then
XtUnmanageChild $(XmMessageBoxGetChild "-" $_DT_TMP_DIALOG_HANDLE \
DIALOG_HELP_BUTTON)
else
XtSetValues $_DT_TMP_DIALOG_HANDLE helpCallback:"$7"
fi
_DtkshPositionDialog "$1"
XtManageChild $_DT_TMP_DIALOG_HANDLE
return 0
}
##############################################################################
#
# This internal shell function performs most of the work required to
# create an instance of a help dialog (regular help or quick help)
# It will reuse an existing instance of the requested type of help
# dialog, if one has already been created; otherwise, it will create
# a new one.
#
# The implied parent of the dialog is identified by the environment
# variable '$TOPLEVEL'.
#
# The incoming parameters are defined as follows (note that $1 and $2 are
# defined by the convenience function which is calling us, while $3 - $6
# are the parameters which were passed by the caller to the convenience
# function:
#
# $1 = existing dialog handle, or "" if first time
# $2 = type of help dialog (Quick or "")
# $3 = dialog title
# $4 = help type
# $5 = help information:
# help volume (if help type = HELP_TYPE_TOPIC)
# help string (if help type = HELP_TYPE_STRING)
# help string (if help type = HELP_TYPE_DYNAMIC_STRING)
# man page name (if help type = HELP_TYPE_MAN_PAGE)
# help file name (if help type = HELP_TYPE_FILE)
# $6 = help location Id (if help type = HELP_TYPE_TOPIC)
#
_DtkshDisplayHelpDialog()
{
typeset helpType ARG1="" ARG2="" ARG3=""
typeset helpType VAL1="" VAL2="" VAL3=""
helpType="${4:-HELP_TYPE_TOPIC}"
ARG1="helpType:"
VAL1="$helpType"
case $helpType in
HELP_TYPE_TOPIC) ARG2="helpVolume:"
VAL2="${5:-}"
ARG3="locationId:"
VAL3="${6:-_HOMETOPIC}";;
HELP_TYPE_STRING) ARG2="stringData:"
VAL2="${5:-}";;
HELP_TYPE_DYNAMIC_STRING) ARG2="stringData:"
VAL2="${5:-}";;
HELP_TYPE_MAN_PAGE) ARG2="manPage:"
VAL2="${5:-}";;
HELP_TYPE_FILE) ARG2="helpFile:"
VAL2="${5:-}";;
*) return 1;;
esac
if [ "$1" = "" ]; then
if [ "$ARG3" != "" ]; then
DtCreateHelp${2}Dialog _DT_TMP_DIALOG_HANDLE $TOPLEVEL "$2" \
"${ARG1}${VAL1}" "${ARG2}${VAL2}" "${ARG3}${VAL3}"
else
DtCreateHelp${2}Dialog _DT_TMP_DIALOG_HANDLE $TOPLEVEL "$2" \
"${ARG1}${VAL1}" "${ARG2}${VAL2}"
fi
else
_DT_TMP_DIALOG_HANDLE=$1
if [ "$ARG3" != "" ]; then
XtSetValues $_DT_TMP_DIALOG_HANDLE \
"${ARG1}${VAL1}" "${ARG2}${VAL2}" "${ARG3}${VAL3}"
else
XtSetValues $_DT_TMP_DIALOG_HANDLE \
"${ARG1}${VAL1}" "${ARG2}${VAL2}"
fi
fi
if [ "$2" = "Quick" ]; then
XtSetSensitive $(DtHelpQuickDialogGetChild "-" $_DT_TMP_DIALOG_HANDLE \
HELP_QUICK_HELP_BUTTON) false
fi
XtSetValues $(XtParent "-" $_DT_TMP_DIALOG_HANDLE) title:"${3:-Help}"
_DtkshPositionDialog "$1"
XtManageChild $_DT_TMP_DIALOG_HANDLE
return 0
}
##############################################################################
#
# This internal shell function takes care of positioning the dialog so
# that it is centered over the window for which it is transient; if the
# window it is transient for is not currently managed, then the window
# will be positioned over in the center of the screen.
#
# Positioning does not occur that first time the dialog is posted; that
# is taken care of automatically by Motif and the window manager. It
# only needs to happen for subsequent postings.
#
_DtkshPositionDialog()
{
typeset -i WIDTH HEIGHT X_P Y_P WIDTH_P HEIGHT_P
typeset -i finalX finalY
if [ "$1" != "" ] && ! XtIsManaged $1 && XtIsShell $TOPLEVEL ; then
XtGetValues $1 width:WIDTH height:HEIGHT
if XtIsRealized $TOPLEVEL; then
XtGetValues $TOPLEVEL x:X_P y:Y_P width:WIDTH_P height:HEIGHT_P
(( finalX=$X_P+($WIDTH_P-$WIDTH)/2 ))
(( finalY=$Y_P+($HEIGHT_P-$HEIGHT)/2 ))
else
(( finalX=($(XWidthOfScreen "-" $(XtScreen "-" $1) )-$WIDTH)/2 ))
(( finalY=($(XHeightOfScreen "-" $(XtScreen "-" $1) )-$HEIGHT)/2 ))
fi
XtSetValues $(XtParent "-" $1) x:$finalX y:$finalY
fi
}