Open Computing ``Hands-On'': ``PC-Unix Connection'' Column: March 94 Listings

Listing 1: Some simple examples of Wksh programs to give you an idea what you can do to add a graphical dimension to your shell programs.

A. A quick Wksh example that creates a box with three push buttons:

#!/usr/bin/wksh -motif

XtAppInitialize TOPLEVEL xmmessages Xmmessages "$@"
XtSetValues $TOPLEVEL geometry:10x10+0+0
  confirm "What?" "echo ok; exit" "echo cancel; exit" "echo help; exit"
XtRealizeWidget $TOPLEVEL
XtMainLoop

B. This example looks more like a beginning X Window System program. It opens a window with a single Motif pushbutton and exits when the button is activated:

#!/usr/bin/wksh -motif

XtAppInitialize TOPLEVEL silly Silly
XtCreateManagedWidget Btn btn pushButton $TOPLEVEL labelString:"Press me"
XtAddCallback $Btn activateCallback "exit 0"
XtRealizeWidget $TOPLEVEL
XtMainLoop

Listing 2. A simple in/out board.

#!/usr/bin/wksh -motif

# TO RUN THIS SCRIPT get your window system running. Call this script
# "example.wksh". Set its execute permission with "chmod u+x example.wksh".
# Run it with "example.wksh". Make sure the wksh command is on your
# system and in your shell command search path (PATH).

# This Windowing Korn Shell example is a companion to the March 1994 issue
# of Unix World's Open Computing magazine, the column "The PC Unix
# Connection" column and script written by Tom Yager
# (tyager@maxx.net). Requires the Windowing Korn Shell (wksh) as
# implemented in System V, release 4.2 with standard Motif toolkit
# bindings.

# Copious comments will help explain this simple application. To move
# beyond that, you'll need two books: "Graphical User Interface Programming"
# and "Windowing System API Reference." Both are published by Prentice-Hall.
# You may also find O'Reilly's book on the X Toolkit to be helpful.

# The first line identifies the shell and the user interface style, here Motif.

# First, initialize the toolkit. A convenience call handles the toolkit init
# and creates the needed top level window. This invisible window provides
# a parent for the application's main window.
# Like all calls that create windows, XtAppInitialize loads a wksh variable
# with a value that references the new window.

NUMROWS=5
let NUMCOLS=NUMROWS+1

# XtAppInitialize    [arguments...]
XtAppInitialize TOPLEVEL uw_example "Tom Yager's Trivial wksh Demo"

# A wksh interface starts with a main window. This can be any supported
# Motif widget, but it's usually a "container widget." These contain
# (through parentage) and organize smaller widgets. In this example, we
# use a rowColumn widget. XtCreateManagedWidget is another convenience
# function that creates an instance of a widget and sets it up to manage
# itself once you make it visible. You may abbreviate XtCreateManagedWidget
# using the alias "cmw".

# XtCreateManagedWidget     [resources...]
XtCreateManagedWidget CONTROLS controls rowColumn $TOPLEVEL \
    orientation:horizontal numColumns:$NUMCOLS packing:PACK_COLUMN

# Resources (colon-separated name:value pairs) define a widget's appearance
# and behavior. See the (3Xm) section of the API Reference for each
# widget's resources.
# Remember to strip off the "XmN" prefix shown in the API reference before
# using a resource in a wksh script.

# Set up column labels across the top. Buttons are used for
# aesthetics' sake; clicking on them does nothing.

cmw LABEL1 label1 pushButton $CONTROLS recomputeSize:false \
    labelString:"In/Out"
cmw LABEL2 label2 pushButton $CONTROLS recomputeSize:false \
    labelString:"Name"
cmw LABEL3 label3 pushButton $CONTROLS recomputeSize:false \
    labelString:"Location"
cmw LABEL4 label4 pushButton $CONTROLS recomputeSize:false \
    labelString:"Returning"
cmw LABEL5 label5 pushButton $CONTROLS recomputeSize:false \
    labelString:"Notes"

# Construct each data row. NUMROWS determines the number of rows this script
# will build. Although this script creates a static number of data rows, you
# can create and destroy widgets dynamically. Consider that your first
# exercise.

# The first element of each row is a pair of checkboxes denoting whether the
# person is in or out. Because the number of columns in the control widget
# are fixed, we must create a single container widget that holds the two
# checkboxes. That container uses just one column. We'll use the
# Exclusives widget. This automatically unselects all other child pushbuttons
# when one is pressed. 

INDEX=1

while ((INDEX <= $NUMROWS)); do

# We'll use the XmCreateRadioBox convenience function. This creates a
# rowColumn widget to hold our in/out toggleButtons. It ensures that only
# one of the buttons is selected at a time.
# XmCreateRadioBox [-m] var parent name
    XmCreateRadioBox -m CHECKS[INDEX] $CONTROLS checks$INDEX \
        orientation:horizontal numColumns:1
    cmw CHECK_IN[INDEX] check_in$INDEX toggleButton ${CHECKS[INDEX]} \
        labelString:"IN"
    cmw CHECK_OUT[INDEX] check_out$INDEX toggleButton ${CHECKS[INDEX]} \
        labelString:"OUT"

    # Then we add three text fields: name, location and returning (the latter
    # for the expected time/date of return).

    cmw NAME[INDEX] name$INDEX textField $CONTROLS columns:20
    cmw LOCATION[INDEX] location$INDEX textField $CONTROLS \
        columns:10
    cmw RETURNING[INDEX] returning$INDEX textField $CONTROLS \
        columns:10

    # Create a pushbutton labeled "Notes...". We'll set this up to pop up a
    # floating window containing a multi-line text editor.

    cmw NOTES[INDEX] notes$INDEX pushButton $CONTROLS labelString:"Notes..."

    # Set up functions to call when in/out checkboxes and the "Notes" buttons
    # are pressed. Pass the row of the control to the function as an argument.
    # It's considered bad form to refer to a function that we haven't
    # defined yet, but the callback expressions aren't evaluated until the
    # control is clicked on ("arm"ed). Callback functions appear below.

    XtAddCallback ${CHECK_IN[INDEX]} armCallback "cb_check_in $INDEX"
    XtAddCallback ${NOTES[INDEX]} armCallback "cb_notes $INDEX"

    let INDEX=INDEX+1
done

# Construct the pop-up window. It won't appear until the user clicks on one
# of the "Notes..." buttons. The CreatePopupShell function is used because
# this is a new top-level window that will appear and disappear apart from
# the others. 

XtCreatePopupShell NOTES_SHELL Notes_shell dialogShell $TOPLEVEL \
    title:"Notes"
sv $NOTES_SHELL height:343 width:294
cw NOTES_FORM Notes_form form $NOTES_SHELL 

# Create an editable text window 

cmw NOTES_TEXT Notes_text text $NOTES_FORM \
    editMode:MULTI_LINE_EDIT rows:20 columns:40

# Create a "Done" button

cw NOTES_DONE Notes_done pushButton $NOTES_FORM \
    `under $NOTES_TEXT 10` labelString:"Done"

# pop up the notes text editor--this function def is not executed until it's
# called
cb_notes() {
    #Retrieve the contents of the "name" text field for the selected row
    #gv is shorthand for XtGetValues
    gv ${NAME[$1]} value:TITLE_NAME
    # Place the name in the title of the text edit window
    # sv is shorthand for XtSetValues
    sv $NOTES_SHELL title:"Notes: $TITLE_NAME"
    XtRealizeWidget $NOTES_SHELL
    XtManageChild $NOTES_DONE
    XtManageChild $NOTES_FORM
}

# Mark someone back in by clearing out text fields on that row
cb_check_in() {
    for a in ${LOCATION[$1]} ${RETURNING[$1]}; do
        sv $a value:""
    done
}

# Tell the toolkit to display the main window and its children when we
# kick off the processing loop.
XtRealizeWidget $TOPLEVEL

# Display the widget tree, manage all managed widgets, and watch for user
# input events. This call never exits; you must terminate the application
# from another location. In this case, it's the callback for the "Quit"
# button. You can also force termination by using the system menu (on the
# left side of the title bar)
XtMainLoop

Copyright © 1995 The McGraw-Hill Companies, Inc. All Rights Reserved.
Edited by Becca Thomas / Online Editor / UnixWorld Online / editor@unixworld.com

[Go to Contents] [Search Editorial]

Last Modified: Wednesday, 17-Jan-96 06:50:58 PST