Example 11.1 is a Java application that displays any image in
the current directory in a viewing area. The viewing area scrolls to accommodate
larger images; the user can use the scrollbars or keypad keys to scroll
the image. In Java 1.1, it is trivial to implement this example with a ScrollPane;
however, if you're using 1.0, you don't have this luxury. Even
if you're using 1.1, this example shows a lot about how to use scrollbars.

Our application uses a Dialog
to select which file to display; a FilenameFilter
limits the list to image files. We use a menu to let the user request a
file list or exit the program. After the user picks a file, the application
loads it into the display area. Figure 11.3
shows the main scrolling window.

The first class contains the FilenameFilter
used to select relevant filenames: that is, files that are likely to contain
GIF, JPEG, or XBM images. If the filename has an appropriate ending, the
accept() method returns true;
otherwise, it returns false.

The ImageListDialog class displays
a list of files from which the user can select. Instead of using FileDialog,
we created a customized List
to prevent the user from roaming around the entire hard drive; choices
are limited to appropriate files in the current directory. When the user
selects an entry (by double-clicking), the image is then displayed in the scrolling
area.

The code in this class is straightforward. The constructor reads the current
directory from the system property list, uses the list()
method of the File class to
create a list of files that match our filename filter, and then creates
a List object that lists these
files. getName() returns the
name of the selected file. action()
is called when the user selects a file; it sets the name of the selected
file from the arg field of
the Event and then calls the
processImage() method of its
parent container. The parent container of our ImageListDialog
is the ScrollingImage class
we are defining; its processImage()
method displays a scrollable image.

The next class, ImageCanvas,
is the Canvas that the image
is drawn onto. We use a separate Canvas
rather than drawing directly onto the Frame
so that the scrollbars do not overlap the edges of the image. You will
notice that the paint() method
uses negative x and y
values. This starts the drawing outside the Canvas
area; as a result, the Canvas
displays only part of the image. This is how we do the actual scrolling.
(xPos, yPos)
are the values given to us from the scrollbars; by positioning the image
at (-xPos, -yPos),
we ensure that the point (xPos,
yPos) appears in the upper
left corner of the canvas.

ScrollingImage provides the
framework for the rest of the program. It provides a menu to bring up the
Dialog to choose the file,
the scrollbars to scroll the scrolling canvas, and the image canvas area.
This class also implements event handling methods to capture the scrollbar
events, paging keys, and menu events.

This handleEvent() method kills
the program if the user used the windowing system to exit from it (WINDOW_DESTROY).
More to the point, if a Scrollbar
generated the event, handleEvent()
figures out if it was the horizontal or vertical scrollbar, saves its value
as the x or y position, and redraws the image in the new location. Finally,
it calls super.handleEvent();
as we will see in the following code, other events that we care about (key events
and menu events) we don't want to handle here--we would
rather handle them normally, in action()
and keyDown() methods.

processImage() reads the image's
filename, calls getImage(),
and sets up a MediaTracker
to wait for the image to load. Once the image has loaded, it reads the
height and width, and uses these to set the maximum values for the vertical
and horizontal scrollbars by calling setValues().
The scrollbars' minimum and initial values are both 0. The size of
the scrollbar "handle" is set to 5, rather than trying to indicate
the visible portion of the image.

action() handles menu events.
If the user selected Open, it displays the dialog box that
selects a file. Close redraws the canvas with a null image;
Quit exits the program. If any of these events occurred,
action() returns true,
indicating that the event was fully handled. If any other event occurred,
action() returns false,
so that the system will deliver the event to any other methods that might
be interested in it.

keyDown() isn't really
necessary, but it adds a nice extension to our scrollbars: in addition
to using the mouse, the user can scroll with the arrow keys. Pressing an
arrow key generates a KEY_ACTION
event. If we have one of these events, we check what kind of key it was,
then compute a new scrollbar value, then call setValue()
to set the appropriate scrollbar to this value. For example, if the user
presses the page up key, we read the page increment, add it to the current
value of the vertical scrollbar, and then set the vertical scrollbar accordingly.
(Note that this works even though nondefault page and line increments
aren't implemented correctly.) The one trick here is that we have
to get the rest of the program to realize that the scrollbar values have
changed. To do so, we create a new SCROLL_ABSOLUTE
event, and call postEvent()
to deliver it.