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.
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.
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.
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.
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:
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.
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:
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.
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.
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.
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.
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.
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.
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:
In the next section, the objects and methods needed to accomplish these tasks are designed.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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. |
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.
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:
The many modifications to the existing classes are listed in Table
19.2.
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 |
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.
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.
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
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.
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];
}
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();
}
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();
}
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;
}
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");
}
}
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.
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.*; |
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");
}
}
}
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.
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.
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();
}
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];
}
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();
}
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();
}
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");
}
}
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;
}
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.
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));
}
}
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.
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
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();
}
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);
}
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);
}
}
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++;
}
}
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;
}
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);
}
}
}
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();
}
}
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.
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.
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"));
}
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.
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);
}
}
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.
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.
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
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));
}
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);
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 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
}
}
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 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.
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:
In addition to verifying that the option appears correctly on the menu, you need to make sure that the application saves work correctly:
The next set of questions focuses on correct functioning of the Cut, Copy, Paste, and Insert options.
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:
Compared with the first two changes, the test plan for the capability to read multiple files is quite short. Test questions include the following:
To test the continuous-play feature, you need to verify both the start and stop of continuous play. The questions to ask follow:
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
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.