# Chapter 19

# Application Upgrades

### CONTENTS

- The Software Development Cycle
  - The Development Process
  - The Maintenance Process
  - Suggestions to Facilitate Modifications
- Conceptualizing Application Upgrades
  - The Build-and-Save Capability
  - Changing the State of the Check Boxes
  - Reading Multiple Files
  - Continuous Play
  - Increasing the Number of Possible Slides
  - Final Task List
- Designing Application Upgrades
  - Build-and-Save
  - Increasing the Number of Possible Slides
  - Adding Audio
  - Summing Up the Object Changes
- Implementing Application Upgrades
  - Changes to SlideCanvas
  - Changes to SlideText
  - Changes to SlidePanel
  - Changes to SlideControls
  - Changes to SlideShow
- The Final Version of SlideShow
- Testing and Debugging
  - Testing the Build-and-Save Capability
  - Testing the Check Boxes
  - Testing the Capability to Read Multiple Files
  - Testing the Continuous-Play Capability
  - Testing the Number of Possible Slides
- Summary

Delivered code is not necessarily finished. This chapter covers application upgrades and maintenance, an important topic because almost every application continues to evolve after its initial delivery. The chapter begins with a discussion of the software life cycle. Then several enhancements are proposed for the `SlideShow` application developed in Chapter 18, "Using Java Applications in the Real World." The remainder of this chapter walks you through the tasks involved in working on these upgrades.

## The Software Development Cycle

Software proceeds through several phases from its first inkling as an idea in someone's mind to a robust application fully installed on a user's system. At each stage the concept of the software is clarified, modified, and expanded. This section begins with a brief review of the software process.

### The Development Process

There are many ways in which the software development process can be broken into stages. For our purposes, consider that software must be analyzed, designed, implemented, tested, and delivered. When these steps are completed, additional changes to the application are considered as upgrades or maintenance.

During analysis, user needs are defined and clarified. For Java applications, this means creating a concrete specification and then expanding it with the details that will be needed to create a complete design.

During the design phase, a means of accomplishing the user needs with appropriate hardware and software is defined. In Java applications, the conceptualization and design process includes the definition of the major classes that will be implemented and any inheritance between those classes.

Implementation refers to the process of actualizing a design by producing code. In Java, this means creating the classes and instances in a manner that can be run by the Java interpreter.

Testing means verifying that the software as implemented meets the design criteria and user specifications. Java applications must have a concrete test plan so the code can be reconciled with the requirements.

Delivery means supplying the user with the software. Java requires that the runtime environment be supplied along with the classes necessary for the implementation.

### The Maintenance Process

Software development does not freeze at the moment of delivery. Usually, software must grow and change over time. These activities are collectively referred to as software maintenance. Application upgrades are part of the normal maintenance phase of the software life cycle. A modification effort is actually a mini project and must proceed through all the phases of the development process.

Peter's Principle
Some maintenance projects are more "mini" than others. Small maintenance/upgrade projects may proceed quickly and informally through all the steps in the development process. For example, modifying a program that calculates loan amortization to run for a 15-year loan instead of a 30-year loan is probably a minor change. However, modifying the same program to allow for a 30-year adjustable-rate loan with an adjustment cap of 2 percent per year, a lifetime cap of 10 percent, and a user-specified yearly index figure is most likely a major change. The second instance requires input and calculations that do not currently exist in the program. The first change merely requires changing some of the variables in the program.

Large maintenance/upgrade projects require careful management and can include plans, designs, and code as large as the initial development effort. At times, the upgrade project may be larger than the original implementation project, yet the upgrade is still called maintenance because it starts with existing code.

There are many reasons for software modification and continued development after the first release. All products evolve, and software is no exception. Applications may need additional features not discovered during the original analysis and design. The software may need fixes to correct problems not discovered during initial development and testing, or the user's needs or environment may change over time.

Applications also can be delivered in increments. In fact, when projects are delivered in increments, they are easier to manage and generally more successful than those for which programmers attempt to deliver the entire project at one milestone. Delivered applications may need to be altered for reasons besides planned incremental releases, and the business reasons governing these changes may affect the modification process.

Java is a new language, so it is still evolving. As new releases come out, you may have to recompile or even reimplement code. Designs may be reworked to take advantage of new features in later releases. Functionality that is currently not available to users may become available. Features implemented in other ways may be incorporated into Java applications-for example, database access using the Java Database Connectivity API.

As new utilities are developed both in-house and externally, they can be incorporated into existing programs. However, portions of applications may have to be reworked to accommodate or match the new utilities.

### Suggestions to Facilitate Modifications

Ease of maintenance is a part of every step in development. If the analysis is complete, users will find the most important features in the first release of the software. If the design is well thought out, modifications will be easier to include. If the code is easy to read and follow, it will be easier to maintain. Some suggestions to ease the modification process follow:

- Try to group changes and deliver another release rather than make incremental changes. Although a new release generally will involve a longer time period than will incremental changes, this gives you time to gain a better understanding of the proposed changes and assess their impact on the application. Having an entire release to work on will help to ensure that the analysis and design phases are done thoroughly. It is easier to assess the impact of minor changes if the entire application is being reviewed for a new release.
- Document all requested changes and the impact of any proposed change. The documentation process will force you to be more thorough about your research. The documentation will also help to point out conflicts among requests by different users. The documentation should supply an idea of the number of hours needed to complete the changes. It also should supply the impact of the request on the existing application and users.
- Have all users involved and in agreement about any modifications before making changes. If this doesn't happen, you may find yourself in the "change it and change it back" syndrome. It is less time-consuming to resolve conflicting opinions while the ideas for changing the application are still on paper.
- Have users assign a priority to each modification. You can reduce stress and conflict if you work on the most critical changes first and save the minor items for later. However, what is critical and what is minor to you may not be the same as what is critical or minor for your user. Sometimes screen design and layout are more critical to users than adding features they are not ready to use or error checking for errors they seldom encounter.
- Some changes may not be possible given current technology, time, and budget constraints. Talk the users through the reasons certain changes are not being implemented. Show them the cost of attempting to implement the change. Provide any information about when the tools might be available to enable you to make the change. For example, at this writing Java does not provide mechanisms for communicating directly with a database server. Although custom scripts could be written to enable a Java application to interact indirectly with a database server, this solution is very time-consuming. However, several people are at work creating this functionality. In a few months, it should be possible to use Java to access a database. So it would probably make sense to delay for a few months any change involving database connectivity.
- The value of experience with code modification cannot be overemphasized. It takes time to gain practice in working with conflicting needs and integrating new code with existing code. The modification process is easier if you work with experienced project managers.

The remainder of this chapter works through modifications to the `SlideShow` application developed in Chapter 18. Several requests for new features in the application are grouped into one new release.

## Conceptualizing Application Upgrades

Before you begin any project, carefully analyze the development requirements to uncover hidden assumptions and resolve conflicts. This process is equally important when enhancing existing applications.

Looking Ahead
In this section, several new requests for the `SlideShow` application are presented and expanded. At the end of this section you should have a good idea of the new tasks that will be added to the application. This will enable you to create a comprehensive design for the modifications.

The following is a prioritized list of change requests for the `SlideShow` application:

- Adding a mechanism to build and save slide shows
- Adding check boxes to controls to turn on and off pictures or text
- Making it possible to read a show consisting of multiple files
- Adding a mechanism to enable continuous play on a timer
- Increasing the possible number of slides that can be stored
- Adding audio
- Adding radio buttons or list boxes to turn audio on and off

The items that are most important to the users are listed at the top of the list. Each item is discussed in more detail in the sections that follow.

### The Build-and-Save Capability

Currently it is tricky to edit the slide show because of the null character that must be used to separate the entries and because the designers of the show are not able to see the show while they are working on it. It would be easier to create a slide show if you could look at the slides and pick and choose which slide to include. It also would be helpful to be able to rearrange the order of slides with a simple cut-and-paste menu similar to that found in word processors.

### Changing the State of the Check Boxes

The options available on the first part of the display menu mirror the options on the control panel at the bottom of the display screen. However, the No Slides and No Text check box menu items are not reproduced on the control panel. It would enhance the design to implement these on the controls. The new items could be check boxes so that the current setting would be displayed to the user as part of the control. This seems like a straightforward addition to the controls because the same methods that are accessed from the menu should be usable by the controls.

### Reading Multiple Files

In the original `SlideShow` application, a show consists of one list of names contained in a single text file. However, there may be times when users will want to load several shows, back to back. Currently, this can be done only by creating a new list of names containing all the slides in the order desired. It would be convenient to be able to list several files on the command line and have the pictures from each loaded.

Tip
If you wanted to load multiple files in the program, this modification would be more complex. In that case, changes would have to be made to the windows controlling the file loading. The current modification, however, does not require any changes to the display. It deals strictly with changes to the handling of the command-line arguments.

Currently, only the first command-line argument is passed to the methods that load the files. The remaining arguments are ignored. To complete this modification, the remaining arguments are passed to the load methods. Then as many slides as will fit in the array are added.

### Continuous Play

The users want the entire slide show to redisplay at regular intervals. The display of the slides should run one at a time after the user presses one button. The user must have a button to stop continuous play and remain on the same slide. The play mechanism will start from the current slide and proceed forward. That way if the user stops the play, he or she can restart it from the same location.

### Increasing the Number of Possible Slides1

Okay, so 20 slides may not be enough for your whole trip to Europe. The application could be rewritten to use a linked list. Then you could increase the number of slides until they meet the memory limits on the computer. However, the easy approach for this one is to increase the size of the array. The array will be increased to hold up to 100 slides.

### Final Task List

Now that you have a good understanding of each of the modifications, you can put together a list of the new tasks that the application must handle. Assuming the modifications will be grouped together and released as an upgrade to the application, you can create one task list for all the desired changes, such as the following:

- Cutting the current slide
- Copying the current slide
- Pasting a slide that has been cut or copied
- Inserting a new slide from a `.gif` file
- Saving a show to a new filename
- Adding check boxes to the controls
- Reading multiple files at startup
- Starting continuous play
- Ending continuous play
- Displaying up to 100 slides in a show
- Playing audio
- Disabling audio
- Enabling audio

In the next section, the objects and methods needed to accomplish these tasks are designed.

## Designing Application Upgrades

At the end of the design you will have a list of new objects to be implemented. Because you are modifying an existing application, your design also should include a list of changes to existing objects. You must clearly define each of these changes.

### Build-and-Save

The build-and-save enhancement is composed of several small changes to the existing application. The first three tasks involve editing the slide show. The design for these tasks is presented subsequently as one topic. The next topic is the design of the insert task. After the show can be edited, a mechanism must be developed to save the files involved in the modified slide show.

#### Editing the Slide Show

The users have been helpful in creating a specification that starts us down the path to design. You know to add a new menu, Edit, that will allow the users to cut, copy, and paste slides. This menu will be used to insert new slides into the slide show.

You must add `Cut`, `Copy`, and `Paste` methods to both `SlideCanvas` and `SlideText`. `SlideCanvas` will need an `Image` object to hold the image most recently cut or copied from its internal array, and `SlideText` will need a `String` object for the same reason.

The `Cut` method will copy a slide to the new object and remove it from the array. The `Copy` method will copy the slide to the new object but will leave it in the array. The `Paste` method will create a new slide in the array and paste the contents of the new object into the new position. However, the `Paste` method will not copy over an existing slide.

The user can delete all the slides in a show, in which case the application has nothing to display. To solve this problem, the `paint()` method for `SlideCanvas` is modified to display a blank rectangle when no slides are present. The `setText()` method for `SlideText` will display an empty string in this case.

#### Inserting New Slides

In addition to rearranging existing slides, the modified application will enable users to add a new slide to the show. The `Insert` menu item will be added in the `Edit` menu. This item will provide a dialog box to the user where he or she can enter the filename of the slide to add.

A File dialog box will be used to get the filename to add. The file must be in the same directory as the executing program. The filename will be passed to an `Insert` method in the `SlidePanel` class in the same manner that the `load` and `save` filenames are passed. The method in `SlidePanel` will remove the file extension and pass the filename to an `Insert` method for each of its two components.

The `SlideCanvas` object's `Insert` method will read a file using the filename and the `.gif` extension. It creates a new `Image` object based on the contents of the file and stores it in the current location in the array. The other items in the array are shifted forward to accommodate the new image.

The `SlideText` `Insert` method will read a file using the filename and the `.txt` extension. It will create a new `String` object and shift the contents of its array forward to accommodate the new object for text.

#### Saving the Modified Show

To save the modified show, you need the filenames for the `.gif` and `.txt` files. Currently, they are loaded when the show is loaded, but they are not stored. Part of this modification will be to modify the code so that updates are stored. This is done by making the arrays that hold the filename's instance arrays in the class rather than local to the `Load` method.

The filenames must be kept in the same order as the slides so that saved shows can be loaded in the proper order. Because slides can be rearranged within the array using the Edit menu items, you must provide a mechanism to rearrange the filenames. Another string is provided to act as a clipboard for the filenames so that they can be manipulated as the slides are manipulated.

The list of filenames must be passed to a method in `SlidePanel`, so you will create an accessor method called `getSlideFiles()` that returns the array of filenames. Another accessor method that returns the number of files currently in the show also will be provided, and the `saveShow` method in `SlidePanel` will be triggered from the menu.

The menu option will open a File dialog box to retrieve the filename where a list of slides in the show will be stored. The filename entered by the user then will be passed to the method in `SlidePanel`.

By default, the slide show names should be saved to a text file. This default can be established by setting the file name to `*.txt` before displaying the dialog box.

Tip
When working with opening and closing files, it is often helpful to review the `FileDialogBox` methods available in the `java.awt` package to remind yourself of details that may be helpful to your users. These methods are reviewed in Chapter 12, "The Java I/O and Utility Class Libraries."

As an added feature, the filename setting for text files can be applied to the Load dialog box. Verify with the users that a filename restriction such as this will be helpful. The restriction makes it much easier to locate the proper file in the dialog box because only files with the `.txt` extension are displayed.

#### Check Boxes

The next enhancement involves a single task adding check boxes at the end of the existing controls. The first check box will have the text "Slides"; the second will have the text "Text." Both will be set by default.

When the Slide check box is cleared, the method `NoSlides()` method in the `displayPanel` is called. Similarly, when the Slide check box is checked, the `setSlides()` method in the `displayPanel` object is called. A corresponding set of method calls is used for the Text check box.

The state of the display can be changed on either the control panel or the menu bar. The menu must inform the controls if it changes the setting of this option, and vice versa. This implies that the menu and the controls each must be informed of the existence of the other.

The event handler for the menu is modified to notify the controls when the display state is changed through the menu. To accomplish this, the event handler must have a means to reference the controls object; therefore, an instance variable is added to store a reference to the controls.

Similarly, the controls are modified to indicate to the menu that a change of state has occurred. To notify the controls, the event is posted to the event handler for the menu. To post the event, the controls must know about the `Menu` object. Therefore, the constructor for the `SlideControls` class is modified to accept a reference to the `MenuBar` object and store the value in an instance variable.

#### Reading Multiple Files

Currently, when the application starts, the `main()` method passes only the first argument from the command line to the `SlideShow` constructor. This will be altered so that all the command-line arguments are passed. The constructor in `SlideShow` then passes the same array to the constructor for `SlidePanel`.

The constructor for `SlidePanel` will be modified to loop through an array of filenames and load each one. The first should go in the front of the array and be loaded normally. The loads of the remaining files need a mechanism to indicate to `LoadShow` that they should be loaded into the array after any existing slides and not on top of them. To provide this mechanism, a boolean argument is added to the `LoadShow` method. When this argument is `false`, the slides are loaded normally. When the argument is `true`, the slides are appended to the current show.

The `LoadShow` methods in both `SlideText` and `SlideCanvas` will be rewritten to accommodate multiple input files. There are two changes in the `LoadShow()` methods: The array counter, which stores the number of files in the array, must account for multiple load files; and the boolean parameter must be checked to determine whether the new slides should be appended to the slides already loaded or should start loading at the first position.

Because it is possible that multiple files will be loaded, the total number of slides in the array is not necessarily the number of slides loaded during a particular call to `LoadShow`. Therefore, the internal counter should not be initialized to zero each time this method is called but rather when an instance of the object is created. For this reason, the initialization is moved to the class level, and the `LoadShow` method sets its internal counter from the instance variable. The counter should be reinitialized when the user selects Load from the File menu.

Currently, there is no difference between the call to `LoadShow` from the `main()` method and the call from the menu. All the existing calls to this method could be modified to pass the second argument, or the method could be overloaded. In this case, if the second argument is not present, it is assumed to be false. Using the second option, there is no need to make changes to the existing code; you can be secure that the new functionality will not affect the existing objects. You now have a well-thought-out design for loading multiple files into one slide show.

#### Continuous Play

To implement the continuous-play mechanism, you will set up a timer that calls the `ShowNextSlide()` method when it expires. The timer will be part of the `SlidePanel` class, so it can be called from the menu, the controls, or both. It will run in its own thread so the user can make changes on the controls, such as canceling continuous play, while it is running.

An instance variable is added to the `SlidePanel` class to determine if the continuous play is currently set. A method will be provided to set this variable and start the timer, and another will clear this variable. The timer will check the variable each time it changes a slide. It will not change the slide and will stop looping if the instance variable indicates that continuous play has been canceled.

It also would be convenient for the timer to recognize when it is at the end of the show and stop looping. Therefore, a method is added in the `SlideCanvas` and `SlideText` classes that returns `true` if the current slide is the last slide and `false` otherwise. The timer will call one of these methods to determine if it is at the last slide.

### Increasing the Number of Possible Slides

Currently, the maximum number of slides that can be stored in the show is 20, based on an array index of 10. This will be increased to allow for a maximum of 100 slides.

Unfortunately, the array index is used in several places throughout the `SlideCanvas` and `SlideText` objects. A global replacement of 20 with a higher number will solve the problem, unless there is a place where the code checks for a number less than or equal to 19 instead of less than 20. In the `SlideShow` application created in Chapter 18, all references are to the number 20. Still, we can prevent possible future problems by assigning a name to the array index.

The array index will be replaced with a static final variable named `ArrayMax`. The variable is static so that all instances of the class use the same value, and final so that its value cannot be changed after initialization. The value is initialized to 100, thus increasing the number of possible slides.

### Adding Audio

It would enhance the appeal and usability of the application to have audio clips of the text messages. Then the user could study the picture while listening to the description instead of having to read the description and then examine the picture. Unfortunately, at this writing, the `play()` and `getAudio()` methods in Java are part of the `Applet` class. It can only be used to play a script if it is given a `baseURL`. Because of time constraints for the project, this request will remain unimplemented until a workaround for this problem is discovered.

Note
If audio support were essential to the application, you would have to create a class of objects for handling audio in applications. As with any other programming language, you would have to create the code for this object from scratch or modify the existing `play()` or `getAudio()` methods to work outside of applets.

### Summing Up the Object Changes

Now that you have an understanding of the upgrade, you can modify the original design documentation for the upgrade. The modified menu categories are shown in Table 19.1.

Table 19.1. Menu features.

Category | Item | Description
File | Load | Loads slides into the application
| Save | Saves filenames of the current slides
| Exit | Exits `SlideShow`
Edit | Cut | Removes current slide and adds to clipboard
| Copy | Adds current slide to clipboard
| Paste | Copies from clipboard, creating a new slide
| Insert | Adds a new slide from a file
Display | Forward | Moves forward to the next slide
| Back | Moves backward to the previous slide
| Beginning | Moves to the first slide in the show
| End | Moves to the last slide in the show
| No Text | When checked, the text is hidden
| No Slides | When checked, the pictures are hidden
Help | Help Topics | Provides a description of the controls
| About | Provides authoring information

The design process includes taking the changes that have been described for each modification and reorganizing them according to the objects that are affected. Two lists are necessary, one for new objects to implement and one for existing objects to modify.

On the basis of the design discussion, you must implement the following objects:

- An `Image` object to store cut slide image
- A `String` object to store cut slide text
- An Edit menu item
- A Cut menu item
- A Copy menu item
- A Paste menu item
- An Insert menu item
- A dialog box for Slide Insert
- An array of image filenames
- An array of text filenames
- A string to store the cut image filename
- A string to store the cut text filename
- A Slide check box
- A Text check box

The many modifications to the existing classes are listed in Table 19.2.

Table 19.2. Changes to existing classes.

Class Name | List of Changes
`SlideCanvas` | Add `Cut` method Add `Copy` method Add `Paste` method Add `Insert` method Add method to return the current slide names Add method to return the current number of slides Modify `LoadShow` to allow multiple input files Increase the number of possible slides to 100
`SlideText` | Add `Cut` method Add `Copy` method Add `Paste` method Add `Insert` method Modify `LoadShow` to allow multiple input files Add a method to return `true` at the end of the show Increase the number of possible slides to 100
`SlidePanel` | Add `Cut` method Add `Copy` method Add `Paste` method Add `Insert` method Add a `Save` method Modify constructor to allow loading of multiple files Add a variable to determine if continuous play is on Add a method to start continuous play Add a method to stop continuous play Add a `Run` method
`SlideControls` | Add Slide check box Add Text check box Modify constructor to accept and store a `MenuBar` object Modify event handler to notify `MenuBar` if state changes via controls
`SlideShow` | Modify menu to include all items in Table 19.1 Modify event handler to handle new items Add an instance variable referring to the current controls Modify event handler to notify controls when state is changed via the menu Modify the `main()` method to pass all input arguments to the constructor Modify the constructor to pass the input arguments to `SlidePanel`

## Implementing Application Upgrades

This section demonstrates how to implement the changes described in the design section. Here, each of the new objects and the modifications for existing objects are implemented.

### Changes to `SlideCanvas`

Given the list of changes created for the `SlideCanvas` class as part of the design, you could just go through and make each of the changes. However, it is important to study how some of the changes affect one another. For example, adding the array to maintain filenames means that this array must be dealt with when the position of images in the image array is changed.

#### Changes to Declarations

Several new components are added to the `SlideCanvas` class. They are protected containers used to store information used by `SlideCanvas`. The declaration for these objects appears as follows:

> `// Image object to store cut slide image
> protected Image SlideClip;
> // Array of image filenames
> protected String[] imgFile = new String[20];
> // String to store cut image filename
> protected String FileClip=new String();`

The declarations for the `NumSlides` and `CurrentSlide` variables are also changed so that they are initialized when they are declared. The new declarations are

> `protected int NumSlides=0; // Number of images in the array
> protected int CurrentSlide=0; // Number of image currently displayed`

#### Adding the `cutSlide` Method

You implement the `cutSlide` method by taking a reference to the current slide; storing it to the internal clipboard, `SlideClip`; and then removing the slide from the array. The complete `cutSlide()` method is shown in Listing 19.1.

> Listing 19.1. The `cutSlide()` method.

> `public void cutSlide(){
> int i;
> //Check that there is an element to cut
> if (NumSlides < 0)
> return;
> //Copy element and its filename to buffer
> SlideClip = im[CurrentSlide];
> FileClip = imgFile[CurrentSlide];
> //Decrease the number of elements in the array
> NumSlides = NumSlides - 1;
> //Shift the array to close the gap
> for (i=CurrentSlide;i<NumSlides;i++) {
> im[i] = im[i+1];
> imgFile[i] = imgFile[i+1];
> }
> if (CurrentSlide == NumSlides)
> CurrentSlide = NumSlides - 1;
> repaint();
> }`

Before cutting a slide, the `cutSlide()` method ensures the slide is present in the array. If no slides are present, it returns to its caller. If a slide is present, it copies to the `SlideClip` object, decreases the number of slides present, and shifts the array contents.

In most cases, when the user cuts a slide, the following slide becomes the current slide. However, if the last slide is clipped-meaning there is no following slide-the current slide is set to the previous slide. This check is made at the end of the `cutSlide` method by comparing the `CurrentSlide` variable and the `NumSlides` variable.

Because you have modified the filename array so it is available to the entire class, you can cut the filename associated with the current slide and save it to `FileClip`. In this way, the two arrays are kept parallel to one another through the cutting and pasting.

#### Adding the `copySlide` Method

You implement the `copySlide` method by copying the current slide into the internal clipboard. Recall that when you copy an object, you do not remove it from the array. The `copySlide()` method is shown in Listing 19.2.

> Listing 19.2. The `copySlide()` method.

> `public void copySlide() {
> //Check that there is an element to copy
> if (NumSlides < 0)
> return;
> SlideClip = im[CurrentSlide];
> }`

#### Adding the `pasteSlide` Method

The `pasteSlide` method creates a new slide in the array and then copies the slide from the internal clipboard to the array. The `pasteSlide()` method is shown in Listing 19.3.

> Listing 19.3. The `pasteSlide()` method.

> `public void pasteSlide() {
> int i;
> //Check that there is an element to paste
> if (SlideClip == null)
> return;
> //Check that there is room to paste an element
> if (NumSlides == 20)
> return;
> //Make a space in the array and the file array
> for(i=NumSlides;i>=CurrentSlide;i--) {
> im[i+1]=im[i];
> imgFile[i+1]=imgFile[i];
> }
> // Increase the number of slides in the show
> NumSlides = NumSlides + 1;
> // Store the items in the clipboard to the new slot
> im[CurrentSlide] = SlideClip;
> imgFile[CurrentSlide]=FileClip;
> repaint();
> }`

#### Adding the `insertSlide` Method

The `insertSlide` method loads a new image using its filename and stores the image in the internal array. If the internal array is full, the method simply returns. If the array is not full, each image is moved forward and the new image is inserted behind the current image. Listing 19.4 shows the code for this method.

> Listing 19.4. The `insertSlide()` method.

> `public void InsertSlide(String FileName) {
> int i;
> //Check that there is room to paste an element
> if (NumSlides == 20)
> return;
> //Make a space in the array and the file array
> for(i=NumSlides;i>=CurrentSlide;i--) {
> im[i+1]=im[i];
> imgFile[i+1]=imgFile[i];
> }
> // Increase the number of slides in the show
> NumSlides = NumSlides + 1;
> // Store the items from the file to the new slot
> imgFile[CurrentSlide]=FileName;
> im[CurrentSlide] = getToolkit().getImage(imgFile[CurrentSlide]+".gif");;
> repaint();
> }`

#### Adding Accessor Methods

The `SlideCanvas` class needs two methods to allow other classes to read, but not write, its protected variables. These methods allow the `SlidePanel` class to save the filenames in the current show. The first method returns the number of slides currently in the show, and the second returns the array containing the names of the slides:

> `public int getNumSlides() {
> return NumSlides;
> }
> public String[] getSlideFiles() {
> return imgFile;
> }`

#### Modifying `LoadShow` to Allow Multiple Input Files

The existing `LoadShow()` method is modified to take a boolean as a second parameter. If the parameter is passed as `true`, the method appends the slides it loads to the slides already in the array. If the parameter is set to `false`, the method overwrites the existing slides with the new slides.

To avoid modifying the existing calls to `LoadShow()`, you will overload the method by adding another `LoadShow()` method that just takes the filename as a parameter. This method simply calls the newly defined `LoadShow(filename, boolean`) method with the second parameter set to `false`. This causes any existing show to be overwritten when a new show is loaded, exactly as these calls worked before you made any changes.

The new `LoadShow` methods for the `SlideCanvas` class appear in Listing 19.5. If you compare this with the original methods, you will notice that the basic operation is the same; the only difference is in the statements that control how the local variable is initialized.

> Listing 19.5. The `LoadShow()` methods.

> `public void LoadShow(String LoadFile){
> LoadShow(LoadFile,false);
> }
> // Load the text array based on filename in the LoadFile
> public void LoadShow(String LoadFile,boolean addfile) {
> RandomAccessFile ListFile;
> int i; // Loop counter
> // Create an array of images and load the images
> if (addfile)
> i=NumTexts;
> else
> i=0;
> try {
> ListFile = new RandomAccessFile(LoadFile,"r");
> while (ListFile.getFilePointer() != ListFile.length() ) {
> strFile[i] = ListFile.readLine();
> i++;
> }
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not read from file ");
> System.out.println(e);
> }
> NumTexts = i;
> for (i=0; i<NumTexts;i++) {
> str[i] = getText(strFile[i]+".txt");
> }
> }`

#### Increasing the Number of Possible Slides to 100

The simplest way to increase the number of slides allowed is to replace all instances of 20 with 100. However, you must then check the class carefully to ensure that there are no cases in which checks are made for the previous counter value, such as i19 instead of i<20.

Cases like this present a problem that can be solved in other languages using constants, but you can't use constants for this in Java. In Java you can create a variable that is both static and final. Therefore, I will create a variable that can hold the value for the number of slides.

The new variable `ArrayMax` is declared in the `SlideCanvas` class. The declaration looks like this:

> `static final int ArrayMax = 100; // Size of array`

All references to the value `20` as an array maximum are then replaced with the name `ArrayMax`.

#### The Complete `SlideCanvas` Class

You have made numerous changes to the `SlideCanvas` class. A complete listing, including all the changes and all the original code, is shown in Listing 19.6.

Note
As you start to work with the classes of the new `SlideShow` application, keep in mind that the new classes represent changes to the existing program. You will find the updated program in its entirety on the CD-ROM. Still, the best way to learn what is happening is to make the changes to each class individually. Before you start, you should copy the existing `SlideShow` application to a new directory on your hard drive. Next, replace the classes that have changed in their entirety with the new code for the class. The `import` statements in the original program should be included in the new application. These `import` statements should read as follows: `import java.awt.*; import java.applet.*; import java.io.RandomAccessFile;`

> Listing 19.6. The complete `SlideCanvas` class.

> `/**
> * SlideCanvas is an extension of Canvas
> * It provides a means to display slides to the user
> */
> class SlideCanvas extends Canvas {
> // Size of array
> static final int ArrayMax = 100;
> // Array of Images to display
> protected Image im[] = new Image[ArrayMax];
> // name of image to display
> protected String[] imgFile = new String[ArrayMax];
> // Number of images in the array
> protected int NumSlides=0;
> // Number of image currently displayed
> protected int CurrentSlide=0;
> // False to turn off display
> protected boolean showslide;
> // Image for cutting & pasting
> protected Image SlideClip;
> // String for cutting & pasting filename
> protected String FileClip=new String();
> public String[] getSlideFiles() {
> return imgFile;
> }
> public int getNumSlides() {
> return NumSlides;
> }
> public boolean endOfShow() {
> if (CurrentSlide == (NumSlides-1))
> return true;
> else
> return false;
> }
> public void cutSlide(){
> int i;
> //Check that there is an element to cut
> if (NumSlides < 0)
> return;
> //Copy element and its filename to buffer
> SlideClip = im[CurrentSlide];
> FileClip = imgFile[CurrentSlide];
> //Decrease the number of elements in the array
> NumSlides = NumSlides - 1;
> //Shift the array to close the gap
> for (i=CurrentSlide;i<NumSlides;i++) {
> im[i] = im[i+1];
> imgFile[i] = imgFile[i+1];
> }
> if (CurrentSlide == NumSlides)
> CurrentSlide = NumSlides - 1;
> repaint();
> }
> public void copySlide() {
> //Check that there is an element to copy
> if (NumSlides < 0)
> return;
> SlideClip = im[CurrentSlide];
> }
> public void pasteSlide() {
> int i;
> //Check that there is an element to paste
> if (SlideClip == null)
> return;
> //Check that there is room to paste an element
> if (NumSlides == ArrayMax)
> return;
> //Make a space in the array and the file array
> for(i=NumSlides;i>=CurrentSlide;i--) {
> im[i+1]=im[i];
> imgFile[i+1]=imgFile[i];
> }
> // Increase the number of slides in the show
> NumSlides = NumSlides + 1;
> // Store the items in the clipboard to the new slot
> im[CurrentSlide] = SlideClip;
> imgFile[CurrentSlide]=FileClip;
> repaint();
> }
> public void InsertSlide(String FileName) {
> int i;
> //Check that there is room to paste an element
> if (NumSlides == ArrayMax)
> return;
> //Make a space in the array and the file array
> for(i=NumSlides;i>=CurrentSlide;i--) {
> im[i+1]=im[i];
> imgFile[i+1]=imgFile[i];
> }
> // Increase the number of slides in the show
> NumSlides = NumSlides + 1;
> // Store the items from the file to the new slot
> imgFile[CurrentSlide]=FileName;
> im[CurrentSlide] = getToolkit().getImage
> Â(imgFile[CurrentSlide]+".gif");;
> repaint();
> }
> public SlideCanvas(String ShowName) {
> super();
> LoadShow(ShowName);
> showslide = true;
> }
> public void NoSlides(){
> showslide = false;
> repaint();
> }
> public void ShowSlides(){
> showslide = true;
> repaint();
> }
> public void paint(Graphics g) {
> Rectangle r = bounds();
> if ((showslide)&&(NumSlides>0))
> g.drawImage(im[CurrentSlide],50,50,r.width-100,r.height-100,this);
> else {
> g.setColor(Color.lightGray);
> g.fillRect(0,0,r.width,r.height);
> }
> }
> public void ShowNextSlide() {
> if (CurrentSlide < (NumSlides - 1)) {
> CurrentSlide = CurrentSlide + 1;
> repaint();
> }
> }
> public void ShowPrevSlide() {
> if (CurrentSlide > 0) {
> CurrentSlide = CurrentSlide - 1;
> repaint();
> }
> }
> public void ShowFirstSlide() {
> CurrentSlide = 0;
> repaint();
> }
> public void ShowLastSlide() {
> CurrentSlide = NumSlides - 1;
> repaint();
> }
> public void LoadShow(String LoadFile){
> LoadShow(LoadFile,false);
> }
> public void LoadShow(String LoadFile, boolean addfile) {
> RandomAccessFile ListFile;
> int i; // Loop counter
> // Create an array of images and load the images
> if (addfile)
> i=NumSlides;
> else
> i=0;
> try {
> ListFile = new RandomAccessFile(LoadFile,"r");
> while (ListFile.getFilePointer() != ListFile.length() ) {
> imgFile[i] = ListFile.readLine();
> i++;
> }
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not read from file.");
> System.out.println(e);
> }
> NumSlides = i;
> for (i=0; i<NumSlides;i++) {
> im[i] = getToolkit().getImage(imgFile[i]+".gif");
> }
> }
> }`

### Changes to `SlideText`

The changes for `SlideText` are similar to the changes for `SlideCanvas`. Again, you can go through the list of changes to this object that are the result of the changes discussed in the "Designing Application Upgrades" section.

#### Changes to Declarations

So that the `SlideText` class is not dependent on the `SlideCanvas` class for any of its functionality, you need to program it to implement its own array of filenames. It also must declare a string to serve as a clipboard for the cut or copied text. These declarations appear as follows:

> `// name of file containing image to display
> protected String[] strFile = new String[20];
> // String for cutting & pasting
> protected String StringClip=new String();
> // String for cutting & pasting filename
> protected String FileClip=new String();`

The initialization of the `NumText` and `CurrentText` variables is also moved to the declaration and appears as follows:

> `protected int NumTexts=0; // Number of strings in the array
> protected int CurrentText=0; // Number of string currently displayed`

Now that you have added all the new objects you will need, you can add the new methods.

#### Adding the `cutString` Method

The `cutString()` method is added to remove the current text string from the array and store it in the newly declared `StringClip` object. The method must remove the corresponding filename for the `strFile` array and store it in `FileClip`. The code to perform these functions is similar to `cutImage` and is shown in Listing 19.7.

> Listing 19.7. The `cutString()` method.

> `public void cutString(){
> int i;
> //Check that there is an element to cut
> if (NumTexts < 0)
> return;
> //Copy element and its filename to buffer
> StringClip = str[CurrentText];
> FileClip = strFile[CurrentText];
> //Decrease the number of elements in the array
> NumTexts = NumTexts - 1;
> //Shift the array to close the gap
> for (i=CurrentText;i<NumTexts;i++){
> str[i] = str[i+1];
> strFile[i] = strFile[i+1];
> }
> if (CurrentText == NumTexts)
> CurrentText = NumTexts - 1;
> setText();
> }`

#### Adding the `copyString` Method

Before copying a text slide, the `Copy` method ensures there is a slide available for copying. It then copies the text to the `StringClip` variable and the filename to the `FileClip` variable. It does not affect the contents of the arrays. Listing 19.8 is the complete code for this method.

> Listing 19.8. The `copyString()` method.

> `public void copyString() {
> //Check that there is an element to copy
> if (NumTexts < 0)
> return;
> StringClip = str[CurrentText];
> }`

#### Adding the `pasteString` Method

Listing 19.9 shows the `pasteString()`method that is created to copy the text from the `SlideText` class's internal clipboard to a new location in the array. Before pasting the text slide, the `pasteString()` method ensures that a string exists to copy and that there is room to insert another string in the array. If both these conditions are true, the method shifts the array forward and creates the new string. The filename where the string is stored is pasted into the filename array so that the show can be saved accurately.

> Listing 19.9. The `pasteString()` method.

> `public void pasteString() {
> int i;
> //Check that there is an element to paste
> if (StringClip == null)
> return;
> //Check that there is room to paste an element
> if (NumTexts == 20)
> return;
> //Make a space in the array and the file array
> for(i=NumTexts;i>=CurrentText;i--){
> str[i+1]=str[i];
> strFile[i+1]=strFile[i];
> }
> // Increase the number of slides in the show
> NumTexts = NumTexts + 1;
> // Store the items in the clipboard to the new slot
> str[CurrentText]=StringClip;
> strFile[CurrentText]=FileClip;
> setText();
> }`

#### Adding the `insertSlide` Method

The purpose of the `insertSlide()` method is to read the text for a new slide from a file on the user's file system. To read a file and return its contents as a text string, the method makes use of the same `getText()` method used by `LoadShow`. Although the filename is passed by the caller, the `insertSlide()` method appends the file extension `.txt` so that only files with the `.txt` extension are read. The new method is shown in Listing 19.10.

> Listing 19.10. The `insertSlide()` method.

> `public void InsertSlide(String Filename) {
> int i;
> //Check that there is room to paste an element
> if (NumTexts == 20)
> return;
> //Make a space in the array and the file array
> for(i=NumTexts;i>=CurrentText;i--){
> str[i+1]=str[i];
> strFile[i+1]=strFile[i];
> }
> // Increase the number of slides in the show
> NumTexts = NumTexts + 1;
> // Read item from the file into the new location
> strFile[CurrentText]=Filename;
> str[CurrentText]=getText(strFile[CurrentText]+".txt");
> setText();
> }`

#### Modifying the `LoadShow()` Method

The `LoadShow()` method for `SlideText` is overloaded just as is `SlideCanvas`. The new overloaded methods are shown in Listing 19.11.

> Listing 19.11. The `LoadShow()` methods.

> `// Overload LoadShow so existing calls don't need to be modified
> public void LoadShow(String LoadFile){
> LoadShow(LoadFile,false);
> }
> // Load the text array based on filename in the LoadFile
> public void LoadShow(String LoadFile,boolean addfile) {
> RandomAccessFile ListFile;
> int i; // Loop counter
> // Create an array of images and load the images
> if (addfile)
> i=NumTexts;
> else
> i=0;
> try {
> ListFile = new RandomAccessFile(LoadFile,"r");
> while (ListFile.getFilePointer() != ListFile.length() ) {
> strFile[i] = ListFile.readLine();
> i++;
> }
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not read from file.");
> System.out.println(e);
> }
> NumTexts = i;
> for (i=0; i<NumTexts;i++) {
> str[i] = getText(strFile[i]+".txt");
> }
> }`

#### Adding the `EndOfShow()` Method

When the automatic-play feature is implemented, the `SlideShow` application must be able to detect when the end of the slide show is reached. To do this, an `endOfShow()` method is added to the `SlideText` class. This method returns `true` to the caller when the last slide is displayed. Listing 19.12 shows the `endOfShow()` method.

Note
`CurrentText` is the index of the current string, and the index values start at zero. `NumTexts` is the total number of strings stored, counting from one. When the `CurrentText` value is one less than the `NumTexts` value, the last slide is in the display window.

> Listing 19.12. The `endOfShow()` method.

> `public boolean endOfShow() {
> if (CurrentText == (NumTexts-1))
> return true;
> else
> return false;
> }`

#### Increasing the Number of Possible Slides

The new static final variable `ArrayMax` also must be declared in the `SlideText` class. The declaration looks like this:

> `static final int ArrayMax = 100; // Size of array`

All references to the value `20` as an array maximum in the `SlideText` class are then replaced with the name `ArrayMax`.

#### The Complete `SlideText` Class

Listing 19.13 shows the complete `SlideText` class with all the new methods and the modifications to the existing methods.

> Listing 19.13. The `SlideText` class.

> `/**
> * SlideText is an extension of TextArea
> * It provides a means to display text descriptions to the user
> */
> class SlideText extends TextArea {
> // Size of array
> static final int ArrayMax = 100;
> // Array of Strings to display
> protected String str[] = new String[ArrayMax];
> // name of file containing image to display
> protected String[] strFile = new String[ArrayMax];
> // Number of strings in the array
> protected int NumTexts=0;
> // Number of string currently displayed
> protected int CurrentText=0;
> // True if text is not being displayed
> protected boolean showtext;
> // String for cutting & pasting
> protected String StringClip=new String();
> // String for cutting & pasting filename
> protected String FileClip=new String();
> public SlideText(String ShowName) {
> super();
> LoadShow(ShowName);
> setEditable(false);
> showtext = true;
> }
> public void cutString(){
> int i;
> //Check that there is an element to cut
> if (NumTexts < 0)
> return;
> //Copy element and its filename to buffer
> StringClip = str[CurrentText];
> FileClip = strFile[CurrentText];
> //Decrease the number of elements in the array
> NumTexts = NumTexts - 1;
> //Shift the array to close the gap
> for (i=CurrentText;i<NumTexts;i++){
> str[i] = str[i+1];
> strFile[i] = strFile[i+1];
> }
> if (CurrentText == NumTexts)
> CurrentText = NumTexts - 1;
> setText();
> }
> public boolean endOfShow() {
> if (CurrentText == (NumTexts-1))
> return true;
> else
> return false;
> }
> public void copyString() {
> //Check that there is an element to copy
> if (NumTexts < 0)
> return;
> StringClip = str[CurrentText];
> }
> public void pasteString() {
> int i;
> //Check that there is an element to paste
> if (StringClip == null)
> return;
> //Check that there is room to paste an element
> if (NumTexts == ArrayMax)
> return;
> //Make a space in the array and the file array
> for(i=NumTexts;i>=CurrentText;i--){
> str[i+1]=str[i];
> strFile[i+1]=strFile[i];
> }
> // Increase the number of slides in the show
> NumTexts = NumTexts + 1;
> // Store the items in the clipboard to the new slot
> str[CurrentText]=StringClip;
> strFile[CurrentText]=FileClip;
> setText();
> }
> public void InsertSlide(String Filename) {
> int i;
> //Check that there is room to paste an element
> if (NumTexts == ArrayMax)
> return;
> //Make a space in the array and the file array
> for(i=NumTexts;i>=CurrentText;i--){
> str[i+1]=str[i];
> strFile[i+1]=strFile[i];
> }
> // Increase the number of slides in the show
> NumTexts = NumTexts + 1;
> // Read item from the file into the new location
> strFile[CurrentText]=Filename;
> str[CurrentText]=getText(strFile[CurrentText]+".txt");
> setText();
> }
> public void NoText(){
> showtext = false;
> setText();
> }
> public void ShowText(){
> showtext = true;
> setText();
> }
> // Override normal setText so text can not be reset accidentally
> public void setText(String str){
> setText();
> }
> public void setText(){
> if ((showtext) && (NumTexts>0))
> super.setText(str[CurrentText]);
> else
> super.setText("");
> }
> public void ShowNextText() {
> if (CurrentText < (NumTexts - 1)) {
> CurrentText = CurrentText + 1;
> setText();
> }
> }
> public void ShowPrevText() {
> if (CurrentText > 0) {
> CurrentText = CurrentText - 1;
> setText();
> }
> }
> public void ShowFirstText() {
> CurrentText = 0;
> setText();
> }
> public void ShowLastText() {
> CurrentText = NumTexts - 1;
> setText();
> }
> public void LoadShow(String LoadFile){
> LoadShow(LoadFile,false);
> }
> // Load the text array based on filename in the LoadFile
> public void LoadShow(String LoadFile,boolean addfile) {
> RandomAccessFile ListFile;
> int i; // Loop counter
> // Create an array of images and load the images
> if (addfile)
> i=NumTexts;
> else
> i=0;
> try {
> ListFile = new RandomAccessFile(LoadFile,"r");
> while (ListFile.getFilePointer() != ListFile.length() ) {
> strFile[i] = ListFile.readLine();
> i++;
> }
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not read from file.");
> System.out.println(e);
> }
> NumTexts = i;
> for (i=0; i<NumTexts;i++) {
> str[i] = getText(strFile[i]+".txt");
> }
> }
> // Read text for an item from a file
> protected String getText(String LoadFile){
> RandomAccessFile ListFile;
> byte b[] = new byte[1024];
> int numbytes;
> try {
> ListFile = new RandomAccessFile(LoadFile,"r");
> numbytes = (int)ListFile.length();
> ListFile.read(b,0,numbytes);
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not read from file.");
> System.out.println(e);
> }
> return (new String(b,0));
> }
> }`

### Changes to `SlidePanel`

The `SlidePanel` class is responsible for using the new methods provided by the `SlideCanvas` and `SlideText` classes. The changes presented for the `SlidePanel` class are from the list completed in the "Designing Application Upgrades" section.

#### Changes to Declarations

To include the autoplay feature, the `SlidePanel` class implements the `Runnable` interface so it can run a timer in a separate thread. This necessitates a change to the class declaration. The thread to run the timer and the boolean variable described in the design are declared as follows:

> `class SlidePanel extends Panel implements Runnable{
> protected boolean keepPlaying; // True when in autoplay
> protected Thread lt; // Local thread for autoplay`

#### Adding the `cutSlide`, `copySlide`, and `pasteSlide` Methods

The `cutSlide`, `copySlide`, and `pasteSlide` methods for the `SlidePanel` class are responsible for calling the corresponding methods of the `SlideCanvas` and `SlideText` objects. The methods containing these calls are shown in Listing 19.14.

> Listing 19.14. The `cutSlide()`, `copySlide()`, and `pasteSlide()` methods for `SlidePanel`.

> `public void cutSlide(){
> displayCanvas.cutSlide();
> displayText.cutString();
> }
> public void copySlide() {
> displayCanvas.copySlide();
> displayText.copyString();
> }
> public void pasteSlide() {
> displayCanvas.pasteSlide();
> displayText.pasteString();
> }`

#### Adding the `InsertSlide` Method

The `InsertSlide` method is only slightly more complex than the `Cut`, `Copy`, and `Paste` methods. The `Insert` method must remove the file extension from the filename it receives so it can pass just the filename to the `Insert` methods for `SlideCanvas` and `SlideText`. To do this, it uses the `indexOf` method found in the `String` class to locate the period (`.`) serving as the separator between the filename and the extension. It then creates a new string from the substring that appears before that character. The complete code is given in Listing 19.15.

> Listing 19.15. The `InsertSlide()` method.

> `public void InsertSlide(String SlideFile) {
> String FilePrefix;
> int sepIndex;
> sepIndex = SlideFile.indexOf(".");
> if (sepIndex < 0 )
> return;
> FilePrefix = SlideFile.substring(0,sepIndex);
> displayCanvas.InsertSlide(FilePrefix);
> displayText.InsertSlide(FilePrefix);
> }`

#### Adding the `saveShow()` Method

The two new methods provided by `SlideCanvas` are used in the `saveShow()` method: The `saveShow` method opens a new file with the filename it is passed and writes the names of the current slides to the file, one per line. Listing 19.16 shows the code for this method.

> Listing 19.16. The `saveShow()` method.

> `public void saveShow(String SaveFile) {
> RandomAccessFile ListFile;
> String[] fileNames;
> int NumFiles;
> int i;
> NumFiles = displayCanvas.getNumSlides();
> fileNames = displayCanvas.getSlideFiles();
> try {
> ListFile = new RandomAccessFile(SaveFile,"rw");
> for (i=0;i<NumFiles;i++)
> ListFile.writeBytes(fileNames[i]+"\n");
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not write to save file "+SaveFile);
> System.out.println(e);
> }
> }`

#### Modifying the Constructor

The constructor of the `SlidePanel` class is modified to allow an array of strings as input. The constructor first calls the `SlideCanvas` and `SlideText` constructors with the first element in this array and then adds a new loop. The loop makes additional calls to `SlideCanvas` and `SlideText` using both the remaining values in the argument list and a second parameter set to `true`. The parameter indicates that the slides in these files should be loaded in addition to the existing slides. The new constructor is shown in Listing 19.17.

> Listing 19.17. The constructor for `SlidePanel`.

> `// Constructor adds objects to the panel
> // Gives each object the filename to load its array from
> public SlidePanel(String ShowName[]) {
> super();
> setLayout(new BorderLayout());
> add("Center",displayCanvas = new SlideCanvas(ShowName[0]));
> add("South",displayText = new SlideText(ShowName[0]));
> int i=1;
> while (i < ShowName.length) {
> displayCanvas.LoadShow(ShowName[i],true);
> displayText.LoadShow(ShowName[i],true);
> i++;
> }
> }`

#### Adding Methods to Start and Stop Continuous Play

The `startPlay()` method sets the instance variable `keepPlaying` and starts a new thread. This creates the first call to the `run` method that will then loop through the slide show. The `stop` method just sets the instance variable `keepPlaying` to `false` and allows the `run()` method to fall out of the loop, terminating the thread. Both of these methods are shown in Listing 19.18.

> Listing 19.18. The `StartPlay()` and `StopPlay()` methods.

> `public void StartPlay(){
> keepPlaying=true;
> lt = new Thread(this);
> lt.start();
> }
> public void StopPlay(){
> keepPlaying=false;
> }`

#### Adding the `run()` Method

The `run()` method added to the `SlidePanel` class implements the timer and loop. It uses the `sleep()` method with an interval of 4,000 milliseconds (4 seconds) to provide a pause between the display of each slide. The method is shown in Listing 19.19.

> Listing 19.19. The `run()` method.

> `public void run(){
> while (keepPlaying && !displayCanvas.endOfShow()) {
> ShowNextSlide();
> try {lt.sleep(4000);}
> catch ( InterruptedException e ) {
> System.out.println("Continous play interrupted by");
> System.out.println(e);
> }
> }
> }`

#### The Complete `SlidePanel` Class

Listing 19.20 shows the complete `SlidePanel` class. The new variables, the new constructor, all the new methods, the old variables, and all the old methods are contained in this listing.

> Listing 19.20. The `SlidePanel` class.

> `/**
> * SlidePanel class holds objects participating in the slideshow
> * Objects are arrays displaying one at a time to the user
> */
> class SlidePanel extends Panel implements Runnable{
> protected SlideCanvas displayCanvas; // Canvas to display .gifs
> protected SlideText displayText; // Text area to display text
> protected boolean keepPlaying; // True when in autoplay
> protected Thread lt; // Local thread for autoplay
> // Constructor adds objects to the panel
> // Gives each object the filename to get load its array from
> public SlidePanel(String ShowName[]) {
> super();
> setLayout(new BorderLayout());
> add("Center",displayCanvas = new SlideCanvas(ShowName[0]));
> add("South",displayText = new SlideText(ShowName[0]));
> int i=1;
> while (i < ShowName.length) {
> displayCanvas.LoadShow(ShowName[i],true);
> displayText.LoadShow(ShowName[i],true);
> i++;
> }
> }
> public void StartPlay(){
> keepPlaying=true;
> lt = new Thread(this);
> lt.start();
> }
> public void run(){
> while (keepPlaying && !displayCanvas.endOfShow()) {
> ShowNextSlide();
> try {lt.sleep(4000);}
> catch ( InterruptedException e ) {
> System.out.println("Continous play interrupted by");
> System.out.println(e);
> }
> }
> }
> public void StopPlay(){
> keepPlaying=false;
> }
> public void InsertSlide(String SlideFile) {
> String FilePrefix;
> int sepIndex;
> sepIndex = SlideFile.indexOf(".");
> if (sepIndex < 0 )
> return;
> FilePrefix = SlideFile.substring(0,sepIndex);
> displayCanvas.InsertSlide(FilePrefix);
> displayText.InsertSlide(FilePrefix);
> }
> public void saveShow(String SaveFile) {
> RandomAccessFile ListFile;
> String[] fileNames;
> int NumFiles;
> int i;
> NumFiles = displayCanvas.getNumSlides();
> fileNames = displayCanvas.getSlideFiles();
> try {
> ListFile = new RandomAccessFile(SaveFile,"rw");
> for (i=0;i<NumFiles;i++)
> ListFile.writeBytes(fileNames[i]+"\n");
> ListFile.close();
> } catch (java.io.IOException e) {
> System.out.println("Could not write to save file "+SaveFile);
> System.out.println(e);
> }
> }
> public void cutSlide(){
> displayCanvas.cutSlide();
> displayText.cutString();
> }
> public void copySlide() {
> displayCanvas.copySlide();
> displayText.copyString();
> }
> public void pasteSlide() {
> displayCanvas.pasteSlide();
> displayText.pasteString();
> }
> public void NoText(){
> displayText.NoText();
> }
> public void ShowText(){
> displayText.ShowText();
> }
> public void NoSlides(){
> displayCanvas.NoSlides();
> }
> public void ShowSlides(){
> displayCanvas.ShowSlides();
> }
> public void LoadShow(String ShowName) {
> displayCanvas.LoadShow(ShowName);
> displayText.LoadShow(ShowName);
> }
> public void ShowNextSlide() {
> displayCanvas.ShowNextSlide();
> displayText.ShowNextText();
> }
> public void ShowPrevSlide() {
> displayCanvas.ShowPrevSlide();
> displayText.ShowPrevText();
> }
> public void ShowFirstSlide() {
> displayCanvas.ShowFirstSlide();
> displayText.ShowFirstText();
> }
> public void ShowLastSlide() {
> displayCanvas.ShowLastSlide();
> displayText.ShowLastText();
> }
> }`

### Changes to `SlideControls`

Now modify the controls to add two new check boxes. The controls must now communicate with the menu bar when the state of either of the check boxes is changed. As with the other classes, the modifications are made as listed in the "Designing Application Upgrades" section.

#### Adding Storage for a `MenuBar`

To notify the menu that a check box changed on the control panel, the controls must know about the `Menu` object. Therefore, the constructor for the `SlideControls` class is modified to accept a `MenuBar` and store the value in an instance variable. This line appears as

> `static MenuBar mb; // Menubar for the show`

The value stored here is loaded in the constructor and used in the event handler.

#### Adding Check Boxes and New Buttons to the Controls

Before you can add check boxes and new buttons for AutoPlay and Stop to the `ShowControls` class, you must modify the constructor for the class to accept a `MenuBar` object and store the value in the new `mbar` object. In this way, the controls know when changes occur on the menu bar.

Next, add the new check boxes to the constructor. A local reference to each control is kept when it is added. This reference is used to set the control's state to `checked`. The final step is to add the AutoPlay and Stop buttons to the constructor.

Listing 19.21 shows the new constructor for the `ShowControls` class.

> Listing 19.21. The constructor for the `ShowControls` class.

> `// Constructor requires a SlidePanel to which the
> // controls are attached
> public ShowControls(SlidePanel theShow) {
> Checkbox cbSlides;
> Checkbox cbText;
> ss = theShow;
> mb = mbar;
> setLayout(new FlowLayout(FlowLayout.CENTER));
> add(new Button("|<<"));
> add(new Button("<"));
> add(new Button(">"));
> add(new Button(">>|"));
> add(cbSlides = new Checkbox("Slides"));
> cbSlides.setState(true);
> add(cbText = new Checkbox("Text"));
> cbText.setState(true);
> add(new Button("AutoPlay"));
> add(new Button("Stop"));
> }`

#### Modifying the Event Handler

Implementing the radio buttons involves modifying the existing event handler for two new event types. The event handler will pass `Checkbox` events to a new method named `handleCheckbox()`and look for `CheckboxMenuItem` events. These events are passed to a new method called `handleCheckboxMenuItem()`. The new event handler appears as follows:

> `public boolean action(Event evt, Object arg) {
> if (evt.target instanceof Button) {
> handleButton((String)arg);
> return true;
> } else if (evt.target instanceof Checkbox) {
> handleCheckbox(((Checkbox) evt.target).getLabel(),(Boolean) arg);
> return true;
> }if(evt.target instanceof CheckboxMenuItem) {
> handleCheckboxMenuItem(evt);
> return true;
> }
> return super.action(evt,arg);
> }
> `

Note
Each class that has actions can have its own type of argument. Notice in the `handleEvent` method that the argument value for a check box is a boolean. This is unlike the `Button` class, in which the argument value in the action method is a string.

The `handleCheckbox` method responds to all `Checkbox` events in the `SlideControls` object. This method uses the instance variable in the `SlideControls` object to access the `SlidePanel`. It then shows or hides the slides for that `SlidePanel` depending on the state of the check box.

The method also posts the event to the event handler for the menu using the `postEvent()` method for the `MenuBar` object stored at the class level. This means the menu is notified when the state changes on the controls.

The `handleCheckbox()` method appears as follows:

> `public void handleCheckbox(Event evt, String cbname,Boolean cbstate){
> if (cbname.equals("Slides")) {
> if (cbstate.equals(Boolean.TRUE)) {
> ss.ShowSlides();
> mb.postEvent(evt);
> } else {
> ss.NoSlides();
> mb.postEvent(evt);
> }
> } else if (cbname.equals("Text")) {
> if (cbstate.equals(Boolean.TRUE)) {
> ss.ShowText();
> mb.postEvent(evt);
> } else {
> ss.NoText();
> mb.postEvent(evt);
> }
> }
> }`

The `handleCheckbox()` method changes the state of the check boxes on the controls in response to events that occur on the menu. When the state changes on the menu, the state of the check boxes must be changed. To do this, a `CheckBoxMenuItem` event is posted to the `ShowControls` object from the method handling the change in the menu. The method to handle these events appears as follows:

> `// Change state of checkboxes if state is different on menu
> public void handleCheckboxMenuItem(Event evt) {
> if (evt.arg.equals("No Text")) {
> if (((CheckboxMenuItem) evt.target).getState() == true)
> ((Checkbox) this.getComponent(5)).setState(false);
> else
> ((Checkbox) this.getComponent(5)).setState(true);
> } else if (evt.arg.equals("No Slides")) {
> if (((CheckboxMenuItem) evt.target).getState() == true)
> ((Checkbox) this.getComponent(4)).setState(false);
> else
> ((Checkbox) this.getComponent(4)).setState(true);
> }
> }`

The event handler can now process state changes from either the controls or the menu. It must also be modified to react to the two new buttons, AutoPlay and Stop, that were added to the controls. The event handler in the `SlideControls` class passes all button-related events to the method `handleButton`. Modify this method to check for either of these buttons and to trigger the appropriate method in the `SlidePanel`. The new `handleButton` method appears as

> `public void handleButton(String bname) {
> if (bname.equals(">")) {
> ss.ShowNextSlide();
> } else if (bname.equals("<")) {
> ss.ShowPrevSlide();
> } else if (bname.equals("|<<")) {
> ss.ShowFirstSlide();
> } else if (bname.equals(">>|")) {
> ss.ShowLastSlide();
> } else if (bname.equals("AutoPlay")) {
> ss.StartPlay();
> } else if (bname.equals("Stop")) {
> ss.StopPlay();
> }
> }`

Figure 19.1 shows the updated controls on the `SlideShow` application. Because both text and graphics are displayed in the figure, the check boxes for slides and text are checked.

Figure 19.1 : The updated controls for SlideShow.

#### The Complete `ShowControls` Class

Although the changes to `ShowControls` are not as extensive as for other classes, a complete code listing, shown in Listing 19.22, can help to clarify them.

> Listing 19.22. The `ShowControls` class.

> `/**
> * Control bar for moving through a slide show
> */
> class ShowControls extends Panel {
> static SlidePanel ss; // Panel controls are attached to
> static MenuBar mb; // Menubar for the show
> // Constructor requires a SlidePanel to which the
> // controls are attached
> public ShowControls(SlidePanel theShow, MenuBar mbar) {
> Checkbox cbSlides;
> Checkbox cbText;
> ss = theShow;
> mb = mbar;
> setLayout(new FlowLayout(FlowLayout.CENTER));
> add(new Button("|<<"));
> add(new Button("<"));
> add(new Button(">"));
> add(new Button(">>|"));
> add(cbSlides = new Checkbox("Slides"));
> cbSlides.setState(true);
> add(cbText = new Checkbox("Text"));
> cbText.setState(true);
> add(new Button("AutoPlay"));
> add(new Button("Stop"));
> }
> // Determine which button was pressed and act accordingly
> // Actions will refer to the Slide panel to which the
> // control bar is attached
> public void handleButton(String bname) {
> if (bname.equals(">")) {
> ss.ShowNextSlide();
> } else if (bname.equals("<")) {
> ss.ShowPrevSlide();
> } else if (bname.equals("|<<")) {
> ss.ShowFirstSlide();
> } else if (bname.equals(">>|")) {
> ss.ShowLastSlide();
> } else if (bname.equals("AutoPlay")) {
> ss.StartPlay();
> } else if (bname.equals("Stop")) {
> ss.StopPlay();
> }
> }
> public void handleCheckbox(Event evt, String cbname, Boolean cbstate) {
> if (cbname.equals("Slides")) {
> if (cbstate.equals(Boolean.TRUE)) {
> ss.ShowSlides();
> mb.postEvent(evt);
> } else {
> ss.NoSlides();
> mb.postEvent(evt);
> }
> } else if (cbname.equals("Text")) {
> if (cbstate.equals(Boolean.TRUE)) {
> ss.ShowText();
> mb.postEvent(evt);
> } else {
> ss.NoText();
> mb.postEvent(evt);
> }
> }
> }
> // Change state of checkboxes if state is different on menu
> public void handleCheckboxMenuItem(Event evt) {
> if (evt.arg.equals("No Text")) {
> if (((CheckboxMenuItem) evt.target).getState() == true)
> ((Checkbox) this.getComponent(5)).setState(false);
> else
> ((Checkbox) this.getComponent(5)).setState(true);
> } else if (evt.arg.equals("No Slides")) {
> if (((CheckboxMenuItem) evt.target).getState() == true)
> ((Checkbox) this.getComponent(4)).setState(false);
> else
> ((Checkbox) this.getComponent(4)).setState(true);
> }
> }
> public boolean action(Event evt, Object arg) {
> if (evt.target instanceof Button) {
> handleButton((String)arg);
> return true;
> } else if (evt.target instanceof Checkbox) {
> handleCheckbox(evt, ((Checkbox) evt.target).getLabel(),
> Â(Boolean) arg);
> return true;
> }if(evt.target instanceof CheckboxMenuItem) {
> handleCheckboxMenuItem(evt);
> return true;
> }
> return super.action(evt,arg);
> }
> }`

### Changes to `SlideShow`

The changes to the `SlideShow` class complete the necessary modifications. The `SlideShow` modifications supply the new menu with a corresponding event handler and allow all command-line arguments to be supplied to the `SlidePanel` class.

#### Modifying the Menu

You can modify the `InitializeMenu()` method so that Edit appears on the menu bar. On the Edit menu you add Cut, Copy, Paste, a separator, and Insert. Because the Paste option is not available until a slide has been cut or copied, you must ensure that its menu item is disabled when the menu is first displayed. The event handler then must enable this menu option after a `Cut` or `Copy` event occurs.

The line to disable the Paste item looks like this:

> `m.getItem(2).disable();`

The `InitializeMenu()` method with the new menu items is shown in Listing 19.23.

> Listing 19.23. The `InitializeMenu()` method.

> `private void InitializeMenu()
> {
> mbar = new MenuBar();
> Menu m = new Menu("File");
> m.add(new MenuItem("Load..."));
> m.add(new MenuItem("Save..."));
> m.addSeparator();
> m.add(new MenuItem("Exit"));
> mbar.add(m);
> m = new Menu("Edit");
> m.add(new MenuItem("Cut"));
> m.add(new MenuItem("Copy"));
> m.add(new MenuItem("Paste"));
> m.addSeparator();
> m.add(new MenuItem("Insert"));
> m.getItem(2).disable();
> mbar.add(m);
> m = new Menu("Display");
> m.add(new MenuItem("Forward"));
> m.add(new MenuItem("Back"));
> m.add(new MenuItem("Beginning"));
> m.add(new MenuItem("End"));
> m.addSeparator();
> m.add(new CheckboxMenuItem("No Text"));
> m.add(new CheckboxMenuItem("No Slides"));
> mbar.add(m);
> m = new Menu("Help");
> m.add(new MenuItem("Help Topics"));
> m.addSeparator();
> m.add(new MenuItem("About SlideShow"));
> mbar.add(m);
> }`

Figure 19.2 shows the new Edit menu for the `SlideShow` application. Because there are no objects on the buffer, the Paste option is dimmed.

Figure 19.2 : The Edit menu for SlideShow.

#### Modifying Declarations

The event handler must communicate with the controls to inform them of state changes on the menu, so a reference to the controls must be available to the event handler. This reference is declared at the class level and set in the constructor for the class. The declaration appears as follows:

> `protected ShowControls SlideControl;// Controls to move through show`

#### Modifying the Constructor

The `SlideShow` constructor is modified to set the `SlideControl` object when it creates the controls. The controls are created using the new constructor for the `ShowControls` class. The `ShowControls` constructor requires a menu bar as a parameter; therefore, the creation of the menu bar is moved to the beginning of the constructor.

The `SlidePanel` constructor is now expecting an array of arguments instead of a single string. Therefore, the `SlideShow` constructor is modified to accept an array of arguments and pass them to `SlidePanel`.

The new version of the `SlideShow` constructor with these three changes appears in Listing 19.24.

> Listing 19.24. The `SlideShow` constructor.

> `public SlideShow(String args[]) {
> InitializeMenu(); // Menu creation moved to the top
> setMenuBar(mbar); // Menu attached to frame
> setBackground(Color.lightGray);
> setLayout(new BorderLayout());
> add("Center",displayPanel = new SlidePanel(args));
> // Set instance variable for controls so it may be called in event handler
> // Pass the controls a reference to the menu
> add("South",SlideControl = new ShowControls(displayPanel, mbar));
> }`

#### Modifying the `main()` Method

The only alteration to the `main()` method is that it should pass the entire array of input arguments instead of only the first item in the array. This amounts to changing the line

> `SlideShow ss = new SlideShow(args[0]);`

to the following:

> `SlideShow ss = new SlideShow(args);`

#### Modifying the Event Handler

Several modifications must be made to the event handler for the `SlideShow` class. The handler must accommodate events for each of the new menu options and the events from the check boxes on the controls.

This is an appropriate time to consider breaking the event handler into smaller objects. Perhaps it would be appropriate to have one method for each type of component as is done in the `SlideControls` object. However, for the frame most of the events involve menu items. Therefore, divide the event handler for `SlideShow` into pieces based on the four menus on the menu bar. A fifth method will handle the state changes from the controls. The event handler still exits the application in response to the `WINDOW_DESTROY` event.

The event handler checks the event target to determine if it is an instance of a menu item or of a check box. If it is neither of these, the event handler for the parent class is called. If the target is a menu item, the event handler must determine the menu where the item appears. The explicit casting makes this check look more complex than it is. For example, the line

> `if (((Menu) ((MenuItem) evt.target).getParent()).getLabel().equals("File")) {`

casts the event target to a menu item and then gets the parent of that menu item. The parent is cast as a menu, and the `getLabel()` method of the menu class is used to get the name of the menu. If the name is File, the statement returns `true`, indicating this is an item on the File menu.

The new event handler appears in Listing 19.25.

> Listing 19.25. The event handler for `SlideShow`.

> `// Handles events in main window
> // Even handlers are supplied for each menu on the menu bar
> public boolean handleEvent(Event evt) {
> if (evt.id == Event.WINDOW_DESTROY &&
> evt.target instanceof SlideShow)
> {
> System.exit(0);
> return true;
> } else if(evt.target instanceof MenuItem) {
> if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("File")) {
> return handleFileEvent(evt);
> } else if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("Edit")) {
> return handleEditEvent(evt);
> } else if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("Display")) {
> return handleDisplayEvent(evt);
> } else if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("Help")) {
> return handleHelpEvent(evt);
> }
> } else if (evt.target instanceof Checkbox) {
> return handleCheckbox(evt, (Checkbox) evt.target);
> }
> return super.handleEvent(evt); // Let parent handle event
> }`

You can now create each of the new methods called in the event handler. These are reviewed in the order they are listed in the event handler.

The `handleFileEvent()` method must allow saves in addition to the loads and exits that were in the original event handler. When the `Save` item is selected on the menu, the event handler responds by creating a new File dialog box. The parent for this box is the current frame, the title is Save Slide Show, and the type is a Save dialog box. After the box is created, the `setFile()` method is used to set the extension for the file to `.txt`. This encourages the user to save the show as a text file. When the user has selected the filename, the new filename and directory are passed to the `saveShow()` method in the `SlidePanel` class.

The complete `handleFileEvent()` method is shown in Listing 19.26.

> Listing 19.26. The `handleFileEvent()` method.

> `// Event handler responds to events from the File menu
> public boolean handleFileEvent(Event evt) {
> if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("Load...")) {
> String FileName;
> FileDialog fd =
> new FileDialog((Frame) this,
> "Get New Slide Show",
> FileDialog.LOAD);
> fd.setFile("*.txt");
> fd.show();
> FileName = fd. getDirectory() + fd.getFile();
> displayPanel.LoadShow(FileName);
> return true;
> } else if (evt.arg.equals("Save...")) {
> String FileName;
> FileDialog fd =
> new FileDialog((Frame) this,
> "Save Slide Show",
> FileDialog.SAVE);
> fd.setFile("*.txt");
> fd.show();
> FileName = fd.getDirectory() + fd.getFile();
> displayPanel.saveShow(FileName);
> return true;
> } else if (evt.arg.equals("Exit")) {
> System.exit(0);
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }`

The `handleEditEvent()` method is used to respond to the items on the edit menu. The Paste item should be enabled only when the Cut or Copy command is selected. The line to do this appears as follows:

> `((Menu)((MenuItem) evt.target).getParent()).getItem(2).enable();`

This line casts the event target as a menu item and uses the `getParent()` method for the `MenuItem` class to get the menu where the item is listed. This an instance of the Edit menu. The Paste command that you want to enable is the third item on this particular Edit menu, but because the index for the menu item starts at zero, the third item has an index of 2. This item is enabled with the `enable` command. Each of these steps could have been written out separately, as in the following code:

> `MenuItem targetItem;
> Menu targetMenu;
> displayPanel.copySlide();
> targetItem = (MenuItem) evt.target;
> targetMenu = (Menu) targetItem.getParent();
> targetMenu.getItem(2).enable();`

The event handler is modified to handle the new items that have been added to the menu. The rewritten event handler appears in Listing 19.27.

> Listing 19.27. The `handleEditEvent()` method.

> `// Event handler responds to events from the Event menu
> public boolean handleEditEvent(Event evt) {
> if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("Cut")) {
> displayPanel.cutSlide();
> ((Menu)((MenuItem) evt.target).getParent()).
> ÂgetItem(2).enable();
> return true;
> } else if (evt.arg.equals("Copy")) {
> ((Menu)((MenuItem) evt.target).getParent()).
> ÂgetItem(2).enable();
> displayPanel.copySlide();
> return true;
> } else if (evt.arg.equals("Paste")) {
> displayPanel.pasteSlide();
> return true;
> } else if (evt.arg.equals("Insert")) {
> String FileName;
> FileDialog fd =
> new FileDialog((Frame) this,
> "Get New Slide Show",
> FileDialog.LOAD);
> fd.setFile("*.gif");
> fd.show();
> FileName = fd.getFile();
> displayPanel.InsertSlide(FileName);
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }`

The `handleDisplayEvent()` method responds to the items on the Display menu. The items to maneuver through the show are handled in the same manner as in the original application. The response to the `CheckboxMenuItems` is modified to post the event to the control panel. The response to the AutoPlay and Stop menu items is to call the appropriate method in the `SlidePanel` class. Listing 19.28 shows the complete `handleDisplayEvent()` method.

> Listing 19.28. The `handleDisplayEvent()` method.

> `// Event handler responds to events from the Display menu
> public boolean handleDisplayEvent(Event evt) {
> if(evt.target instanceof CheckboxMenuItem) {
> if (evt.arg.equals("No Text")) {
> if (((CheckboxMenuItem) evt.target).getState() == true){
> displayPanel.NoText();
> SlideControl.postEvent(evt);
> } else {
> displayPanel.ShowText();
> SlideControl.postEvent(evt);
> }
> return true;
> } else if (evt.arg.equals("No Slides")) {
> if (((CheckboxMenuItem) evt.target).getState() == true) {
> displayPanel.NoSlides();
> SlideControl.postEvent(evt);
> } else {
> displayPanel.ShowSlides();
> SlideControl.postEvent(evt);
> }
> return true;
> }
> } else if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("Forward")) {
> displayPanel.ShowNextSlide();
> return true;
> } else if (evt.arg.equals("Back")) {
> displayPanel.ShowPrevSlide();
> return true;
> } else if (evt.arg.equals("Beginning")) {
> displayPanel.ShowFirstSlide();
> return true;
> } else if (evt.arg.equals("End")) {
> displayPanel.ShowLastSlide();
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }`

The response to the items on the Help menu does not change. However, because the event handler is divided into several methods, a new method, shown in Listing 19.29, is created to handle these events.

> Listing 19.29. The `handleHelpEvent()` method.

> `// Event handler responds to events from the Help menu
> public boolean handleHelpEvent(Event evt) {
> if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("About SlideShow")) {
> AboutBox ab = new AboutBox(this);
> ab.show();
> return true;
> } else if (evt.arg.equals("Help Topics")) {
> HelpBox ab = new HelpBox(this);
> ab.show();
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }`

The `SlideShow` object modifies the state of the check boxes on the menu in response to changes on the controls. These are `Checkbox` events that are handled in the new method `handleCheckbox()`. This method checks the name and state of the check box and modifies the current state of the menu accordingly. The new method appears in Listing 19.30.

> Listing 19.30. The `handleCheckbox()` method.

> `public boolean handleCheckbox(Event evt, Checkbox cb) {
> boolean cbstate = cb.getState();
> String cbname = cb.getLabel();
> if (cbname.equals("Slides")) {
> if (cbstate == true)
> ((CheckboxMenuItem) mbar.getMenu(2).getItem(6)).setState(false);
> else
> ((CheckboxMenuItem) mbar.getMenu(2).getItem(6)).setState(true);
> return true;
> } else if (cbname.equals("Text")) {
> if (cbstate == true)
> ((CheckboxMenuItem) mbar.getMenu(2).getItem(5)).setState(false);
> else
> ((CheckboxMenuItem) mbar.getMenu(2).getItem(5)).setState(true);
> return true;
> }
> return super.handleEvent(evt);
> }`

#### The Complete `SlideShow` Class

The `SlideShow` class now includes all the changes discussed in this section. Listing 19.31 shows the code for the complete class.

> Listing 19.31. The `SlideShow` class.

> `/**
> * Creates Window with panel for the slideshow
> * plus a Menu and controls for the SlideShow
> */
> public class SlideShow extends Frame {
> protected MenuBar mbar; // Menu for the slideshow
> protected SlidePanel displayPanel; // Panel to display the show
> protected ShowControls SlideControl;// Controls to move through show
> public SlideShow(String args[]) {
> InitializeMenu();
> setMenuBar(mbar);
> setBackground(Color.lightGray);
> setLayout(new BorderLayout());
> add("Center",displayPanel = new SlidePanel(args));
> add("South",SlideControl = new ShowControls(displayPanel, mbar));
> }
> // Creates the menu and attaches it to the MenuBar
> private void InitializeMenu()
> {
> mbar = new MenuBar();
> Menu m = new Menu("File");
> m.add(new MenuItem("Load..."));
> m.add(new MenuItem("Save..."));
> m.addSeparator();
> m.add(new MenuItem("Exit"));
> mbar.add(m);
> m = new Menu("Edit");
> m.add(new MenuItem("Cut"));
> m.add(new MenuItem("Copy"));
> m.add(new MenuItem("Paste"));
> m.addSeparator();
> m.add(new MenuItem("Insert"));
> m.getItem(2).disable();
> mbar.add(m);
> m = new Menu("Display");
> m.add(new MenuItem("Forward"));
> m.add(new MenuItem("Back"));
> m.add(new MenuItem("Beginning"));
> m.add(new MenuItem("End"));
> m.addSeparator();
> m.add(new CheckboxMenuItem("No Text"));
> m.add(new CheckboxMenuItem("No Slides"));
> mbar.add(m);
> m = new Menu("Help");
> m.add(new MenuItem("Help Topics"));
> m.addSeparator();
> m.add(new MenuItem("About SlideShow"));
> mbar.add(m);
> }
> // Event handler responds to events from the File menu
> public boolean handleFileEvent(Event evt) {
> if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("Load...")) {
> String FileName;
> FileDialog fd =
> new FileDialog((Frame) this,
> "Get New Slide Show",
> FileDialog.LOAD);
> fd.setFile("*.txt");
> fd.show();
> FileName = fd. getDirectory() + fd.getFile();
> displayPanel.LoadShow(FileName);
> return true;
> } else if (evt.arg.equals("Save...")) {
> String FileName;
> FileDialog fd =
> new FileDialog((Frame) this,
> "Save Slide Show",
> FileDialog.SAVE);
> fd.setFile("*.txt");
> fd.show();
> FileName = fd.getDirectory() + fd.getFile();
> displayPanel.saveShow(FileName);
> return true;
> } else if (evt.arg.equals("Exit")) {
> System.exit(0);
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }
> // Event handler responds to events from the Event menu
> public boolean handleEditEvent(Event evt) {
> if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("Cut")) {
> displayPanel.cutSlide();
> ((Menu)((MenuItem) evt.target).getParent()).getItem(2).enable();
> return true;
> } else if (evt.arg.equals("Copy")) {
> ((Menu)((MenuItem) evt.target).getParent()).getItem(2).enable();
> displayPanel.copySlide();
> return true;
> } else if (evt.arg.equals("Paste")) {
> displayPanel.pasteSlide();
> return true;
> } else if (evt.arg.equals("Insert")) {
> String FileName;
> FileDialog fd =
> new FileDialog((Frame) this,
> "Get New Slide Show",
> FileDialog.LOAD);
> fd.setFile("*.gif");
> fd.show();
> FileName = fd.getFile();
> displayPanel.InsertSlide(FileName);
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }
> // Event handler responds to events from the Display menu
> public boolean handleDisplayEvent(Event evt) {
> if(evt.target instanceof CheckboxMenuItem) {
> if (evt.arg.equals("No Text")) {
> if (((CheckboxMenuItem) evt.target).getState() == true){
> displayPanel.NoText();
> SlideControl.postEvent(evt);
> } else {
> displayPanel.ShowText();
> SlideControl.postEvent(evt);
> }
> return true;
> } else if (evt.arg.equals("No Slides")) {
> if (((CheckboxMenuItem) evt.target).getState() == true) {
> displayPanel.NoSlides();
> SlideControl.postEvent(evt);
> } else {
> displayPanel.ShowSlides();
> SlideControl.postEvent(evt);
> }
> return true;
> }
> } else if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("Forward")) {
> displayPanel.ShowNextSlide();
> return true;
> } else if (evt.arg.equals("Back")) {
> displayPanel.ShowPrevSlide();
> return true;
> } else if (evt.arg.equals("Beginning")) {
> displayPanel.ShowFirstSlide();
> return true;
> } else if (evt.arg.equals("End")) {
> displayPanel.ShowLastSlide();
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }
> // Event handler responds to events from the Help menu
> public boolean handleHelpEvent(Event evt) {
> if(evt.target instanceof MenuItem) {
> if (evt.arg.equals("About SlideShow")) {
> AboutBox ab = new AboutBox(this);
> ab.show();
> return true;
> } else if (evt.arg.equals("Help Topics")) {
> HelpBox ab = new HelpBox(this);
> ab.show();
> return true;
> }
> }
> return super.handleEvent(evt); // Let parent handle event
> }
> public boolean handleCheckbox(Event evt, Checkbox cb) {
> boolean cbstate = cb.getState();
> String cbname = cb.getLabel();
> if (cbname.equals("Slides")) {
> if (cbstate == true)
> ((CheckboxMenuItem) mbar.getMenu(2).
> ÂgetItem(6)).setState(false);
> else
> ((CheckboxMenuItem) mbar.getMenu(2).
> ÂgetItem(6)).setState(true);
> return true;
> } else if (cbname.equals("Text")) {
> if (cbstate == true)
> ((CheckboxMenuItem) mbar.getMenu(2).
> ÂgetItem(5)).setState(false);
> else
> ((CheckboxMenuItem) mbar.getMenu(2).
> ÂgetItem(5)).setState(true);
> return true;
> }
> return super.handleEvent(evt);
> }
> // Handles events in main window
> // Even handlers are supplied for each menu on the menu bar
> public boolean handleEvent(Event evt) {
> if (evt.id == Event.WINDOW_DESTROY &&
> evt.target instanceof SlideShow)
> {
> System.exit(0);
> return true;
> } else if(evt.target instanceof MenuItem) {
> if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("File")) {
> return handleFileEvent(evt);
> } else if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("Edit")) {
> return handleEditEvent(evt);
> } else if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("Display")) {
> return handleDisplayEvent(evt);
> } else if (((Menu) ((MenuItem) evt.target).getParent()).
> ÂgetLabel().equals("Help")) {
> return handleHelpEvent(evt);
> }
> } else if (evt.target instanceof Checkbox) {
> return handleCheckbox(evt, (Checkbox) evt.target);
> }
> return super.handleEvent(evt); // Let parent handle event
> }
> // main method used to start application
> public static void main(String args[]){
> // Verify that there is a filename to attempt to load show
> if (args.length <= 0) {
> System.out.println("Usage: java SlideShow filename.");
> System.exit(0);
> }
> // Create instance of this object
> SlideShow ss = new SlideShow(args);
> ss.setTitle("Slide Show"); // Set the title of the frame
> ss.pack(); // Pack components
> Dimension d;
> d = Toolkit.getDefaultToolkit().getScreenSize();
> ss.resize(d.width, d.height);
> ss.show(); // Display the frame
> }
> }`

## The Final Version of `SlideShow`

Referring back to the design, you can see that the completed modification includes all the changes listed. All the new objects have been added. The modifications to existing objects have been completed. The menu items listed in the design now appear and function on the working application. For easy reference, the application is available on the CD-ROM that accompanies this book. Also, each class-level object has its own section in this chapter, so you can refer to them individually for reference if necessary.

The `SlideShow` application with its modifications represents a real-world development project. Adding the features in this chapter should have given you insight into how simple applications can evolve into large projects over time.

## Testing and Debugging

Testing is even more crucial for application upgrades than it is for the initial release. At this point, your users are accustomed to the way the original application operates and do not want any surprise changes. Your thorough testing should ensure that no such surprises occur. The users will also be paying careful attention to the places where the application has changed. Again, your tests should make sure they see what they are expecting.

To test an upgrade thoroughly, create a completely new set of tests. Document these tests for the same reasons that you documented the test plan for the original application. In addition to testing the new features, you should also run the tests from the initial installation. This verifies that all the features originally provided still work.

It is useful to have a separate directory to run tests. This keeps the results from being confused with those of a previous test. Wait until all the modifications for a release are completed before beginning testing. Just as with new development, perform all testing before correcting problems.

The remainder of this section provides a brief set of tests for each change implemented.

### Testing the Build-and-Save Capability

The build-and-save capability affects several portions of the application. Tests must be provided for all the new features. The first set of questions deals with the options added to the menu:

- Does the Edit menu appear on the menu bar?
- Is the Cut option available on the Edit menu?
- Is the Copy option available on the Edit menu?
- Is the Paste option present but dimmed on the Edit menu when the program first starts?
- Is the Insert option available on the Edit menu?
- Is the Save option available on the File menu?
- After a slide is copied with the Copy option, is the Paste option available?
- After a slide is cut with the Cut option, is the Paste option available?

In addition to verifying that the option appears correctly on the menu, you need to make sure that the application saves work correctly:

- Does the Save option produce a File dialog box and allow the user to save a file?
- Can the user load a file that has been saved in a prior session?

The next set of questions focuses on correct functioning of the Cut, Copy, Paste, and Insert options.

- Do the picture and text disappear from the screen when the Cut option is selected?
- Are the picture and text inserted prior to the current picture when the Paste option is selected after the Cut option?
- Do the picture and text remain on the screen when the Copy option is selected?
- Are the picture and text inserted prior to the current picture and text when the Paste option is selected after the Copy option?
- Does the Insert option bring up a File dialog box?
- Does the File dialog box from the Insert option allow you to select a `.gif` file to load?
- When a file is selected in the File dialog box from the Insert option, do the picture and text load into the show prior to the current slide?

### Testing the Check Boxes

Build-and-save has the longest test plan because it is changed so extensively. The check boxes do not take as long to verify. However, care must still be taken to verify the interaction between the check boxes and the menu. Suggested test questions include the following:

- When the Slides box is cleared, is the display of pictures hidden?
- When the Text box is cleared, is the display of text hidden?
- When the Slides box is checked, are the pictures displayed again?
- When the Text box is checked, is the text displayed again?
- When the Slides box is checked, is the No Slides menu option unchecked?
- When the Slides box is cleared, is the No Slides menu option checked?
- When the Text box is checked, is the No Text menu option unchecked?
- When the Text box is cleared, is the No Text menu option checked?
- When the Slides box is checked, is the No Slides menu option unchecked?
- When the No Slides menu option is checked, is the Slides box cleared?
- When the No Slides menu option is cleared, is the Slides box checked?
- When the No Text menu option is checked, is the Text box cleared?
- When the No Text menu option is cleared, is the Text box checked?

### Testing the Capability to Read Multiple Files

Compared with the first two changes, the test plan for the capability to read multiple files is quite short. Test questions include the following:

- When the command line contains a list of two or more files to load, are they visible in the program?
- When the command line contains only one file, is it correctly loaded into the program?

### Testing the Continuous-Play Capability

To test the continuous-play feature, you need to verify both the start and stop of continuous play. The questions to ask follow:

- When the AutoPlay button is selected, are new slides displayed on a four-second interval?
- When the Stop button is selected, does the screen remain on the same slide?

### Testing the Number of Possible Slides

Changing the number of slides that can be held internally will not result any obvious changes to the application. However, it is still important to verify that the change accomplished what is needed. The test plan for this modification is simply

- Can a show containing more than 20 slides be loaded into the program?

## Summary

This chapter covers several ideas relating to the modification of existing applications. Various upgrades to the `SlideShow` application have been developed to serve as examples of how applications can be modified and to further demonstrate the capabilities and limitations of the Java language.

At this point you should be comfortable with reading and understanding existing Java code. You should be able to create new classes by extending existing classes and adding features. You have covered most of the techniques involved in building Java applications and can create your own intermediate applets and applications. You are ready to move on to building advanced applets and applications.