Chapter 24

Scrollbars


CONTENTS


This chapter is dedicated to explaining the use of the Scrollbar class. It describes the methods provided by this class and shows how to use vertical and horizontal scrollbars to scroll text and graphics displayed in a window. The event handling supported by scrollbars is also explained. When you finish this chapter, you will be able to effectively use scrollbars in your Java window programs.

How Scrollbars Work

Scrollbars are used to scroll through an object contained in a window that is too large to be completely displayed within the window. Vertical scrollbars are used to scroll up and down from the beginning of the object to its end. Horizontal scrollbars are used to scroll right and left between the right and left sides of the object. Horizontal and vertical scrollbars are implemented in the same fashion. The only differences between them are their placement around the object to be scrolled and the direction in which they move the scrolled object through the window.

Users interact with scrollbars in three ways. By clicking on the arrows at the end of the scrollbars, they move the scrolled object one scrollable unit in the opposite direction of the arrow. This causes the window to appear as though it has moved over the object in the direction of the arrow.

Note
A scrollable unit is defined by the application program. Vertical and horizontal units differ. Most text-processing applications define a vertical unit as a single line of text and a horizontal unit as a percentage of the horizontal screen size. Graphical applications define vertical and horizontal units as a percentage of the visible screen size.

Scrollbars contain tabs that identify the relative location of the object being viewed with respect to the total size of the object. By clicking between the tab and the end of a scrollbar, the view of the object is updated in a one-page increment in the direction of the arrow. This is the second way that scrollbars can be used. The definition of a page is also application specific. Vertical scrollbars usually define a page as the vertical size of the viewing window or some percentage of this size. Horizontal scrollbars tend to operate in the same manner. A horizontal page is defined as a fixed percentage of the horizontal viewing area.

The third way that a user can interact with a scrollbar is to drag the scrollbar's tab to a specific location within the scrollbar. When the user drags the tab of a vertical scrollbar, he moves the object being viewed up or down in the viewing window. When the user drags the tab of a horizontal scrollbar, he moves the object being viewed left or right in the viewing window.

Using Scrollbars

When you use scrollbars in your Java programs, you will most likely be using them to scroll through a Graphics object that is associated with a Canvas object or the main application window. You create and place scrollbars in your window in the same manner as any other window component. Their position and size within the window are determined by the layout associated with the window.

Scrollbars are created using the Scrollbar() constructor. Three forms of this constructor are provided. The default constructor takes no parameters and is not particularly useful unless you want to create a Scrollbar object and then specify its orientation and use later in your program. The second constructor allows the orientation of a Scrollbar object to be specified. The third Scrollbar() constructor uses the five parameters that are needed to create a working scrollbar: orientation, value, visible, minimum, and maximum.

The orientation of a scrollbar is specified by the VERTICAL and HORIZONTAL constants defined by the Scrollbar class. The minimum and maximum parameters specify the minimum and maximum values associated with the scrollbar's position. These values should map to the object being scrolled. For example, if you are scrolling a 1000-line text object, appropriate minimum and maximum values for a vertical scrollbar would be 0 and 999. Horizontal values could be determined using the maximum width of the text to be scrolled (in pixels).

The value parameter identifies the starting value associated with the scrollbar. The value parameter is usually set to the minimum value of the scrollbar. However, suppose you wanted to initiate the display of an object with its center displayed on the screen. You would then set the scrollbar's value parameter to the average of its minimum and maximum values.

The visible parameter is used to specify the size of the viewable area of the object being scrolled. For example, if you are scrolling a 1000-line text object, and the viewable area of the window is 25 lines long, you should set the visible variable to 25.

The Scrollbar class provides several methods for getting and setting the parameters of a Scrollbar object. The getOrientation(), getValue(), getVisible(), getMinimum(), and getMaximum() methods retrieve the parameter values discussed so far. The getValue() method is used to determine to what position the user has scrolled.

The setLineIncrement() and setPageIncrement() methods are used to specify the size of a scrollable unit and page relative to the minimum and maximum values associated with a scrollbar. For example, when scrolling text, you can set the line increment of a vertical scrollbar to 1 so that only one line of text is vertically scrolled. You can set the page increment to 10 to allow 10 lines of text to be scrolled when the user clicks between the tab and arrows of a scrollbar. The getLineIncrement() and getPageIncrement() methods provide access to the current line- and page-increment values.

The setValue() method allows you to directly set the current position of a scrollbar. The setValues() method allows you to specify a scrollbar's value, visible, minimum, and maximum parameters.

In order to respond to user scrollbar operations and implement scrolling of the object associated with a scrollbar, you must handle the events generated by user manipulation of the scrollbar. These events are the SCROLL_LINE_DOWN, SCROLL_LINE_UP, SCROLL_PAGE_DOWN, SCROLL_PAGE_UP, and SCROLL_ABSOLUTE events.

The SCROLL_LINE_UP event is generated when the user clicks on the up arrow of a vertical scrollbar or on the left arrow of a horizontal scrollbar. The SCROLL_LINE_DOWN event is generated when the user clicks on the down arrow of a vertical scrollbar or the right arrow of a horizontal scrollbar. The SCROLL_PAGE_UP event is generated when the user clicks between the up arrow of a vertical scrollbar and its tab. It is also generated when a user clicks between the left arrow of a horizontal scrollbar and its tab. Similarly, the SCROLL_PAGE_DOWN event is generated when the user clicks between the down arrow of a vertical scrollbar and its tab, or between the right arrow of a horizontal scrollbar and its tab. These events pass an argument that specifies the number of lines that are scrolled.

The SCROLL_ABSOLUTE event is generated when a user drags the tabs of a scrollbar to a new position. This event passes an argument that identifies the new scrollbar position.

Scrolling Text

In order to handle scrollbar events and implement scrolling, you must repaint the area of the scrollable window based on the mapping between the object being scrolled and the vertical and horizontal scrollbar positions. If you are scrolling text, you are most likely displaying the text on the Graphics object associated with a Canvas object or the main application window. The TextArea object implements its own scrollbars and does not require any additional event handling.

When scrolling text vertically, you should adjust the minimum and maximum values of the vertical scrollbar based on the number of lines of text that are contained in the object being scrolled. In this way, when the user moves the scrollbar's tab to its topmost position, the beginning of the text is displayed, and when the user moves the tab to the bottom of the scrollbar, the end of the text is displayed.

When scrolling text horizontally, you should set the minimum and maximum values of the horizontal scrollbar based on the maximum width of the text being displayed.

The TextScrollApp Program

The TextScrollApp program shows how scrollbars can be used to scroll text that is drawn on a Graphics object. This program provides the capability to read in a text file and display it on the screen using the default 12-point plain font. It allows the text file to be scrolled vertically and horizontally through the application window. The source code of the TextScrollApp program is shown in Listing 24.1.


Listing 24.1. The source code of the TextScrollApp program.

import java.awt.*;
import java.io.*;
import java.util.Vector;
import jdg.ch20.*;

public class TextScrollApp extends Frame {
 Object menuItems[][] = {{"File","Open","-","Exit"}};
 MyMenuBar menuBar = new MyMenuBar(menuItems);
 FileDialog openFile = new FileDialog(this,"Open File",FileDialog.LOAD);
 Font defaultFont = new Font("default",Font.PLAIN,12);
 int screenWidth = 400;
 int screenHeight = 400;
 Vector text = new Vector();
 int topLine;
 Toolkit toolkit;
 FontMetrics fm;
 int baseline;
 int lineSize;
 int maxWidth;
 Scrollbar hbar, vbar;
 public static void main(String args[]){
  TextScrollApp app = new TextScrollApp();
 }
  public TextScrollApp() {
  super("TextScrollApp");
  setup();
  pack();
  resize(screenWidth,screenHeight);
  show();
 }
 void setup() {
  setBackground(Color.white);
  setMenuBar(menuBar);
  setupFontData();
  setupScrollbars();
 }
 void setupFontData() {
  setFont(defaultFont);
  toolkit = getToolkit();
  fm = toolkit.getFontMetrics(defaultFont);
  baseline = fm.getLeading() + fm.getAscent();
  lineSize = fm.getHeight();
 }
 void setupScrollbars() {
  hbar = new Scrollbar(Scrollbar.HORIZONTAL,0,0,0,0);
  vbar = new Scrollbar(Scrollbar.VERTICAL,0,0,0,0);
  hbar.setBackground(Color.lightGray);
  vbar.setBackground(Color.lightGray);
  add("South",hbar);
  add("East",vbar);
 }
 void resetScrollbars() {
  hbar.setValues(0,10,0,maxWidth+5);
  vbar.setValues(0,10,0,text.size()+5);
 }
 public void readFile(String file) {
  DataInputStream inStream;
  try{
   inStream = new DataInputStream(new FileInputStream(file));
  }catch (IOException ex){
   notifyUser("Error opening file");
   return;
 }
 try{
  Vector newText = new Vector();
  String line;
  maxWidth = 0;
  while((line=inStream.readLine())!=null) {
   int lineWidth = fm.stringWidth(line);
   if(lineWidth > maxWidth) maxWidth = lineWidth;
   newText.addElement(line);
  }
  text = newText;
  topLine = 0;
  inStream.close();
  resetScrollbars();
  repaint();
 }catch (IOException ex){
  notifyUser("Error reading file");
 }
}
public void notifyUser(String s) {
  String text[] = {s};
  String buttons[] = {"OK"};
  new Notification(this,"Error",true,text,buttons);
 }
 public void paint(Graphics g) {
  topLine = vbar.getValue();
  int xOffset = hbar.getValue();
  int numLines = text.size();
  screenHeight = size().height;
  int y = baseline;
  for(int i = topLine;(i < numLines) && (y < screenHeight + lineSize);++i) {
   g.drawString((String) text.elementAt(i),-xOffset,y);
   y += lineSize;
  }
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   String s = (String) event.arg;
   if(event.target instanceof MenuItem){
    if("Exit".equals(s)){
     System.exit(0);
      return true;
    }else if("Open".equals(s)){
     openFile.show();
     if(openFile.getFile() != null) {
      String inFile = openFile.getFile();
      readFile(inFile);
     }
     return true;
    }
   }
  }else if(event.id == Event.SCROLL_LINE_UP ||
   event.id == Event.SCROLL_LINE_DOWN ||
   event.id == Event.SCROLL_PAGE_UP ||
   event.id == Event.SCROLL_PAGE_DOWN ||
   event.id == Event.SCROLL_ABSOLUTE) repaint();
   return false;
 }
}
class Notification extends MessageDialog {
 public Notification(Frame parent,String title,boolean modal,
   String text[],String buttons[]) {
  super(parent,title,modal,text,buttons);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   dispose();
   return true;
  }else if(event.id==Event.ACTION_EVENT && event.target instanceof Button){
   dispose();
   return true;
  }
  return false;
 }
}


When you run the TextScrollApp program, you will see the opening window shown in Figure 24.1. Notice that vertical and horizontal scrollbars are added to the application window.

Figure 24.1 : The TextScrollApp opening window.

Click on the File pull-down menu and select the Open menu item. An Open File dialog box is displayed. Open the TextScrollApp.java file. The window is updated, as shown in Figure 24.2.

Figure 24.2 : Displaying TextScrollApp.java.

Use the horizontal scrollbar to scroll the view of the text to the right. (See Figure 24.3.)

Figure 24.3 : Scrolling horizontally.

Scroll the text all the way back to the left and then click once on the down arrow of the vertical scrollbar. This causes the window to move the view of the text down one line. (See Figure 24.4.)

Figure 24.4 : Scrolling vertically one line.

Click on the vertical scrollbar between the tab and the down arrow at the bottom of the scrollbar. This results in the view of the text to be scrolled down 10 lines. (See Figure 24.5.)

Figure 24.5 : Scrolling vertically using SCROLL_PAGE_DOWN.

Now drag the tab of the vertical scrollbar to the middle of the scrollbar. The view of the text is scrolled to the middle of the text file. (See Figure 24.6.)

Figure 24.6 : Scrolling vertically with the tab.

Experiment with both the vertical and horizontal scrollbars to familiarize yourself with their operation before going on to study the TextScrollApp program.

The TextScrollApp program declares a number of field variables. The menuItems[] array and menuBar variable are used to construct the program's menu bar. The openFile variable implements the dialog box used to load in the text file to be displayed. The defaultFont variable is used to set the text font to a default 12-point plain style font. The screenWidth and screenHeight variables are used to specify the dimensions of the application window.

The text variable is declared as a Vector object. It is used to store the individual lines of text that are read from the text file. The topLine variable identifies the number of the line that is currently displayed at the top of the window. The toolkit variable is used to refer to the Toolkit object associated with the application window. The fm variable is used to refer to the FontMetrics object associated with the default font.

The baseline variable is the vertical offset where the baseline of the first line of text should be displayed. The lineSize variable refers to the total height of the font being displayed. The maxWidth variable is calculated when a file is read. It refers to the maximum length of a text line in pixels. The hbar and vbar variables identify the horizontal and vertical scrollbars that are created and attached to the main window.

The setupFontData() method sets the window's font to the default font identified with the defaultFont variable. It then obtains the Toolkit object associated with the window and uses the getFontMetrics() method of the Toolkit to get the FontMetrics object associated with the default font. This object is assigned to the fm variable. The baseline variable is set to the sum of the leading and ascent of the current font as returned by the getLeading() and getAscent() methods of the FontMetrics class. The lineSize variable is set to the total height of the font using the getHeight() method of the FontMetrics class.

The setupScrollbars() method creates and initializes the horizontal and vertical scrollbars. A horizontal scrollbar is created with all of its parameters set to zero and is assigned to the hbar variable. A vertical scrollbar is created in the same manner and is assigned to the vbar variable. The specific parameters associated with these scrollbars are set when the text file that is to be scrolled is initially loaded.

The background color of both scrollbars is set to light gray, and then the scrollbars are added to the main application window. Using BorderLayout simplifies the positioning of the scrollbars.

The resetScrollbars() method resets the parameters associated with both scrollbars based on the text that is loaded from the file. The horizontal scrollbar assigned to hbar is assigned a minimum value of 0 and a maximum value of maxWidth + 5, where maxWidth is the maximum width of a text line in pixels. The constant 5 is added to maxWidth to allow some scrolling to the right of the end of the widest line. The value parameter of the scrollbar is set to 0 so that the leftmost end of a text line is initially visible. The visible parameter is set to 10 to allow horizontal scrolling of 10 pixels at a time.

The vertical scrollbar assigned to vbar is initialized by setting its maximum parameter to the number of text lines plus the constant 5. The number of text lines is determined by invoking the size() method of the Vector class for the text Vector used to hold the text that was loaded from the selected text file. Specifying the visible parameter to 10 results in 10 lines being scrolled at a time. Note that the parameters of the horizontal scrollbar are specified in pixels and those of the vertical scrollbar are specified in text lines.

The readFile() method is almost the same as the one used in previous examples. It has been modified to store each line of text that is read within the vector that is assigned to the text variable.

The paint() method is used to display the text Vector on the screen. This method is also indirectly invoked in response to user scrolling actions. The paint() method sets the topLine variable to the value of the vertical scrollbar. It obtains this value by invoking the getValue() method of the Scrollbar class for vbar. It sets the xOffset variable to the value of the horizontal scrollbar. The scrollbar values are maintained and updated internally by the Scrollbar objects.

The numLines variable is set to the number of lines of text that are stored in the text Vector. The screenHeight variable is recalculated to adjust the text display for any window-resizing operations that may have occurred.

The y variable is used to specify the vertical position where each line of text should be drawn on the Graphics object passed to the g parameter when paint() is invoked. A for statement is used to draw the text. The text drawing begins with the line specified by the topLine variable and continues until the last line of the text file is displayed or the vertical display position assigned to the y variable is one line past the end of the screen. The drawString() method of the Graphics class is used to draw the text on the screen. It is invoked with the text lines stored in the Vector object assigned to the text variable. The elementAt() method of the Vector class is used to retrieve the required line of text. Note that the xOffset variable is passed to the drawString() method as a negative value. This causes text drawing to begin before the left edge of the display window and enables horizontal scrolling toward the right to be implemented.

The handleEvent() method handles the SCROLL_LINE_UP, SCROLL_LINE_DOWN, SCROLL_PAGE_UP, SCROLL_PAGE_DOWN, and SCROLL_ABSOLUTE events by simply invoking the repaint() method to cause the screen to be repainted. The scrollbars maintain the value of their current positions. These values are used by the paint() method when the screen is repainted.

Scrolling Graphics

The scrolling of graphics is handled in the same way as text. The only difference between text scrolling and graphics scrolling is that vertical text scrolling results in an integral number of lines being scrolled at a time and graphics scrolling does not. The minimum and maximum values associated with a vertical text scrollbar are generally set based on the number of lines to be scrolled. The parameters of the horizontal and vertical scrollbars associated with graphics applications are set based on the dimensions of the Graphics object being scrolled and the size of the window in which the object is being viewed.

The ImageScrollApp Program

The ImageScrollApp program shows how scrollbars can be used to scroll objects that are drawn on a Graphics object. This program upgrades the DrawApp program developed in Chapter 23, "The Canvas," with the capability to support horizontal and vertical scrolling. The source code of the ImageScrollApp program is shown in Listing 24.2.


Listing 24.2. The source code of the ImageScrollApp program.

import java.awt.*;
import java.lang.Math;
import java.util.Vector;
import jdg.ch20.*;

public class ImageScrollApp extends Frame {
  Object menuItems[][] = {
    {"File","New","-","Exit"},
    {"Draw","+Line","-Oval","-Rectangle"}
  };
  MyMenuBar menuBar = new MyMenuBar(menuItems);
  MyCanvas canvas = new MyCanvas(TwoPointObject.LINE);
  int screenWidth = 400;
  int screenHeight = 400;
  int canvasWidth = 1000;
  int canvasHeight = 1000;
  Scrollbar hbar, vbar;
  public static void main(String args[]){
    ImageScrollApp app = new ImageScrollApp();
  }
  public ImageScrollApp() {
    super("ImageScrollApp");
    setup();
    pack();
    resize(screenWidth,screenHeight);
    show();
  }
  void setup() {
    setBackground(Color.white);
    setMenuBar(menuBar);
    setCursor(CROSSHAIR_CURSOR);
    add("Center",canvas);
    setupScrollbars();
 }
 void setupScrollbars() {
   hbar = new Scrollbar(Scrollbar.HORIZONTAL,0,10,0,canvasWidth);
   vbar = new Scrollbar(Scrollbar.VERTICAL,0,10,0,canvasHeight);
   hbar.setBackground(Color.lightGray);
   vbar.setBackground(Color.lightGray);
   add("South",hbar);
   add("East",vbar);
  }
  public boolean handleEvent(Event event) {
   if(event.id==Event.WINDOW_DESTROY){
     System.exit(0);
     return true;
  }else if(event.id==Event.GOT_FOCUS && (event.target instanceof MyCanvas)) {
     setCursor(CROSSHAIR_CURSOR);
     return true;
  }else if(event.id==Event.LOST_FOCUS && (event.target instanceof MyCanvas)) {
     setCursor(DEFAULT_CURSOR);
     return true;
  }else if(event.id == Event.SCROLL_LINE_UP ||
     event.id == Event.SCROLL_LINE_DOWN ||
     event.id == Event.SCROLL_PAGE_UP ||
     event.id == Event.SCROLL_PAGE_DOWN ||
     event.id == Event.SCROLL_ABSOLUTE) {
       canvas.updateOffsets(hbar.getValue(),vbar.getValue());
       return true;
  }else if(event.id==Event.ACTION_EVENT){
     if(event.target instanceof MenuItem){
       String arg = (String) event.arg;
       if(processFileMenu(arg)) return true;
       if(processDrawMenu(arg)) return true;
     }
   }
   return false;
  }
  public boolean processFileMenu(String s) {
   if("New".equals(s)){
     canvas.clear();
     return true;
   }else if("Exit".equals(s)){
     System.exit(0);
     return true;
   }
   return false;
  }
  public boolean processDrawMenu(String s) {
    MyMenu menu = menuBar.getMenu("Draw");
    CheckboxMenuItem lineItem = (CheckboxMenuItem) menu.getItem("Line");
    CheckboxMenuItem ovalItem = (CheckboxMenuItem) menu.getItem("Oval");
    CheckboxMenuItem rectangleItem =
      (CheckboxMenuItem) menu.getItem("Rectangle");
    if("Line".equals(s)){
      canvas.setTool(TwoPointObject.LINE);
      lineItem.setState(true);
      ovalItem.setState(false);
      rectangleItem.setState(false);
     return true;
   }else if("Oval".equals(s)){
     canvas.setTool(TwoPointObject.OVAL);
     lineItem.setState(false);
     ovalItem.setState(true);
     rectangleItem.setState(false);
     return true;
   }else if("Rectangle".equals(s)){
     canvas.setTool(TwoPointObject.RECTANGLE);
     lineItem.setState(false);
     ovalItem.setState(false);
     rectangleItem.setState(true);
     return true;
   }
   return false;
  }
 }
 class MyCanvas extends Canvas {
   int tool = TwoPointObject.LINE;
   Vector objects = new Vector();
   TwoPointObject current;
   boolean newObject = false;
   int xOffset = 0;
   int yOffset = 0;
   public MyCanvas(int toolType) {
    super();
    tool = toolType;
  }
  public void setTool(int toolType) {
    tool = toolType;
  }
  public void clear() {
    objects.removeAllElements();
    repaint();
  }
  public void updateOffsets(int x,int y) {
    xOffset = x;
    yOffset = y;
    repaint();
  }
  public boolean mouseDown(Event event,int x,int y) {
    current = new TwoPointObject(tool,x+xOffset,y+yOffset);
    newObject = true;
    return true;
  }
  public boolean mouseUp(Event event,int x,int y) {
    if(newObject) {
      objects.addElement(current);
      newObject = false;
    }
    return true;
  }
  public boolean mouseDrag(Event event,int x,int y) {
   int newX = x + xOffset;
   int newY = y + yOffset;
   if(newObject) {
     int oldX = current.endX;
     int oldY = current.endY;
     if(tool != TwoPointObject.LINE) {
     if(newX > current.startX) current.endX = newX;
       if(newY > current.startY) current.endY = newY;
       int width = Math.max(oldX,current.endX) - current.startX + 1;
       int height = Math.max(oldY,current.endY) - current.startY + 1;
       repaint();
     }else{
       current.endX = newX;
       current.endY = newY;
       int startX = Math.min(Math.min(current.startX,current.endX),oldX);
       int startY = Math.min(Math.min(current.startY,current.endY),oldY);
       int endX = Math.max(Math.max(current.startX,current.endX),oldX);
       int endY = Math.max(Math.max(current.startY,current.endY),oldY);
       repaint();
     }
    }
   return true;
  }
  public void paint(Graphics g) {
   int numObjects = objects.size();
   for(int i=0;i<numObjects;++i) {
     TwoPointObject obj = (TwoPointObject) objects.elementAt(i);
     obj.draw(g,xOffset,yOffset);
  }
  if(newObject) current.draw(g,xOffset,yOffset);
 }
}
class TwoPointObject {
  public static int LINE = 0;
  public static int OVAL = 1;
  public static int RECTANGLE = 2;
  public int type, startX, startY, endX, endY;
  public TwoPointObject(int objectType,int x1,int y1,int x2,int y2) {
    type = objectType;
    startX = x1;
    startY = y1;
    endX = x2;
    endY = y2;
  }
  public TwoPointObject(int objectType,int x,int y) {
  this(objectType,x,y,x,y);
  }
  public TwoPointObject() {
    this(LINE,0,0,0,0);
  }
  public void draw(Graphics g,int xOffset,int yOffset) {
    if(type == LINE)
     g.drawLine(startX - xOffset,startY - yOffset,endX - xOffset,endY -yOffset);
  else{
     int w = Math.abs(endX - startX);
     int l = Math.abs(endY - startY);
     if(type == OVAL) g.drawOval(startX - xOffset,startY - yOffset,w,l);
     else g.drawRect(startX - xOffset,startY - yOffset,w,l);
    }
   }
  }


When you run the ImageScrollApp program, the window shown in Figure 24.7 is displayed. This program should look similar to the DrawApp program in Chapter 23. The DrawApp program has been upgraded to support vertical and horizontal scrolling.

Figure 24.7 : The ImageScrollApp opening window.

Use the Draw menu to select drawing tools, and then draw some objects in the visible window as I have done in Figure 24.8.

Figure 24.8 : Drawing some objects.

Use the vertical and horizontal scrollbars to move throughout the extended canvas and draw more objects as I have done in Figure 24.9.

Figure 24.9 : Scrolling and drawing more.

Experiment with the scrollbars and drawing tools to become more familiar with the program's operation.

The ImageScrollApp program upgrades the DrawApp program to use scrollbars. Because the operation of the DrawApp program is extensively described in the previous chapter, I'll assume that it is still fresh in your mind and only describe the coding differences required to implement the scrollbars.

The ImageScrollApp class uses the canvasWidth and canvasHeight variables to represent the horizontal and vertical dimensions of the canvas. The area of the canvas is over six times the area of the default window size. This provides enough canvas to implement a reasonable amount of scrolling. The hbar and vbar variables are used to identify the horizontal and vertical scrollbars.

The setupScrollBars() method creates a horizontal scrollbar with the maximum size parameter set to the canvasWidth. It assigns this object to the hbar variable. A vertical scrollbar is created with its maximum parameter set to the canvasHeight and assigned to the vbar variable. The visible parameter of both scrollbars is set to the value of 10. This enables page up and down operations to scroll 10 pixels at a time, a relatively small distance.

The handleEvent() method handles all scrollbar-related events by invoking the updateOffsets() method of the MyCanvas class with the current values of the horizontal and vertical scrollbars. The method is invoked for the MyCanvas object assigned to the canvas variable.

The MyCanvas class adds two new variables to its definition. The xOffset and yOffset variables maintain the current position of the canvas that is displayed in the upper-left corner of its Graphics object. These variables are modified via the updateOffsets() method, which also invokes the repaint() method to cause the canvas to be repainted.

The xOffset and yOffset variables are used in the mouseDown() and mouseDrag() event- handling methods to translate the x,y-coordinates supplied as arguments in the method invocation to coordinates that are relative to the overall canvas.

The mouseDrag() method has also been simplified to completely repaint the screen rather than limiting repainting to the local area being updated.

The paint() method adds the xOffset and yOffset variables to the draw() method used to draw objects of the TwoPointObject class.

The draw() method of the TwoPointObject class uses the xOffset and yOffset parameters to translate the x,y-coordinates of the objects being drawn from coordinates that are relative to the overall canvas to coordinates that are relative to the current Graphics object being updated.

Summary

This chapter explains the use of the Scrollbar class. It shows you how to use vertical and horizontal scrollbars to scroll the text and graphics displayed on the canvas. The event handling supported by scrollbars has also been explained. Chapter 25, "Using Animation," shows you how to add multimedia features, such as sound and animation, to your Java programs.