# Chapter 21

# Designing and Implementing Advanced Applications

### CONTENTS

- Project Development and Planning
  - Determining Requirements
  - Defining the Necessary Objects
  - Plotting the Project to Completion
- Project Design
- Project Implementation
  - Containers for the Objects
  - Building the Main Window
  - Adding the Menu Bar with Pull-down Menus
  - Adding the Choice Menus
  - Adding the Text Area
  - Adding File Objects
  - Setting the Wheels in Motion
  - Handling Events
  - Building the Utility Functions
  - Handling File I/O
  - Displaying the Dialog Boxes
  - Building the Dialog Boxes
  - Text-Manipulation Objects
- The Complete Project
- Summary

Java is a programming language that knows no bounds. Just as you can create advanced applications in other programming languages, you can do so in Java as well. This chapter takes you step by step through the development of an advanced application called Jompanion.

Jompanion is a companion editor for Java programming that includes many features found in advanced applications. Whereas previous chapters have focused primarily on programming concepts, this chapter focuses on design concepts. The design of your application's interface tells the user a lot about the program itself. Well-designed applications feature friendly graphical interfaces complete with pull-down menus, choice menus, and pop-up windows.

Because the Jompanion editor has a well-thought-out design, it is the perfect example for illustrating design issues. The editor also includes a complete set of advanced functions that you can use in just about any application you create.

## Project Development and Planning

Although you can often create a small application on a whim, you should create advanced applications only with careful forethought. The more complex the project, the more involved your planning should be. For most projects, planning involves mapping out the steps necessary to complete the project on paper. For very complex projects you might want to use a project-management tool such as Microsoft Project to make the planning process easier.

Each step is generally allocated a time period, such as six days for planning or eight days for implementation. Some steps are dependent on other steps, meaning they cannot be started until other aspects of the project are completed. Other steps are not dependent on any other steps and can be performed at any time during the project's development. Therefore, when you map out the steps necessary to complete the project, you should always note which steps are dependent on others and which are independent.

After you plot out the steps that will take you through project completion, you can group the steps into phases that form the software development life cycle for the application. Generally, most projects include six phases:

- Requirements phase: This phase is when you determine project needs by first examining the purpose, scope, and audience of the project and then evaluating your reasonable expectations for the project. Finally, you translate these needs, goals, and purposes into project requirements.
- Specification/Analysis phase: During this phase, you determine the inner workings of the application. You can do this in a traditional manner, through specification diagrams such as data flow diagrams or state transition diagrams, or you can simply write out the programming logic. You should also define the necessary objects and the functions they will perform.
- Planning phase: During the planning phase, you take the requirements and specifications developed in the previous phases and plot out the steps necessary to complete the project.
- Design phase: The design phase is when you design the layout of the project in more detail than you did in the specification phase. For example, you might create outlines for classes and methods.
- Implementation phase: During the implementation phase, you actually create the project you have developed. This phase often takes the longest period of time.
- Testing phase: During the testing phase, you ensure the project performs as it was designed. Ideally, testing is an ongoing phase throughout the development of the project. In fact, when you complete each phase you should verify the materials you have generated.

The duration of each phase should be relevant to the size and complexity of the application you are developing. You might be able to develop and implement a project with limited scope in a single day, but for more advanced projects this process can take days, weeks, or months.

Although each phase should be performed in order, you can sometimes combine phases if it suits your needs. For Jompanion, I combined the requirements, specification, and planning phases into a general phase called development and planning.

### Determining Requirements

When you begin a new project, the first thing you should do is develop a list of project needs. If you have not created a project folder, you should do so now. The project folder will hold everything related to the project. Generally, your project folder will have both a paper and an electronic form. The paper form of the folder can be a three-ring binder or a note pad. The electronic version of the folder should reside on your hard drive as a directory.

To help you develop a list of project requirements, you should first identify the purpose, scope, and target user for the application. Your purpose statement should describe the type of application you are creating and why you are creating it. The scope of the project relates to its size and functionality. The target user is the person or group for whom you are developing the application.

Often you will have primary and secondary target users. The primary target users are those who will use the application. The secondary target users are those who may purchase or review the application. For example, although children would use an educational application, adults would purchase it. The children using the application would want it to be fun. The adults buying the application would want it to help the children learn.

The purpose, scope, and target user for Jompanion are defined as follows:

> Purpose:

> A companion editor for Java programming

> Scope:

> An advanced application with many features

> Target user:

> Anyone who plans to do Java programming and wants an easy-to-use text editor.

Next, make a list of constraints for the project. Constraints that apply to most projects include duration, size, and budget. Normally, you will specify the duration of the project if you have a deadline. When you have a set amount of time or money to invest in the project, you will want to constrain the budget. You might also want to limit the size of the application, especially if performance and memory use are a concern. The initial constraints for Jompanion are

> Project duration:

> No more than 2 weeks

> Project size:

> 25-50KB

> Project budget:

> 40-50 hours

After you have determined the project constraints, you can develop a list of needs. Project needs include personnel, computer hardware and software, financial resources, and supplies. For Jompanion, the list of needs is small:

- A computer with the Java Developer's Kit installed on it
- Disk space on hard drive
- Floppy disks to back up the project
- Daily time investment

### Defining the Necessary Objects

After determining project requirements, you should create an overview of the inner workings of the application by defining the necessary objects and the flow of data between the objects. The way you map out the objects and their flow will largely depend on the type of application you are developing, the modeling tools you are using, and the development philosophy at the organization where you work. For Jompanion, I borrowed some of the concepts used in structured analysis.

Preliminary modeling in structured analysis is done in the environmental model, which helps you define the interfaces between your application and the user. The environmental model includes three key components:

- Statement of purpose-Provides a brief description of the application for the management staff.
- Context diagram-Provides an overview of the interaction between the application and outside entities.
- Event list-Provides a complete list of events that can occur and to which the application must respond.

You will find that the components of the environmental model are useful for most projects and can be modified to suit almost any type of project. Earlier I defined a brief statement of purpose for Jompanion. I expanded upon it for use by management as follows:

> Statement of Purpose:

> The purpose of the Jompanion text editor is to be a companion editor for Java development. The editor includes advanced features that make it useful for development, but it uses minimal system resources.

The context diagram defines the external entities with which the application interacts. You will find that the more complex the system, the more useful the diagram. As you can see in Figure 21.1, the only entities Jompanion interacts with are the user and the file system.

Figure 21.1 : Context diagram for Jompanion.

The most useful component of the environmental model is the event list. The event list for Jompanion provides a complete list of events it should support. You use this list to help you define objects for your application. Events for Jompanion include

- Create a new file
- Open a file
- Save current file
- Save current file with a new name
- Find a keyword or phrase
- Replace a keyword or phrase with another keyword or phrase
- Replace all occurrences of a keyword or phrase with another keyword or phrase
- Cut text and save to a buffer
- Copy text and save to a buffer
- Paste text from the buffer
- Change font type
- Change font size
- Provide help or overview information
- Inform user when errors occur
- Allow user to resize the window if necessary
- Quit or exit the editor

After you develop the environmental model for your application, you can go on to more detailed modeling. This is when you translate the event list into data flow, input, and output. Whether you use data-flow diagrams, state-transition diagrams, petri nets, or some other type of data models will primarily depend on the type of application you are developing.

### Plotting the Project to Completion

After you have modeled the project, you can plan it. The first thing you should do in the planning stage is work out a schedule for the project. The schedule does not have to be absolute, however; the best schedules are flexible and include the necessary milestones, goals, and time allocations to take the project through to completion.

The purpose of the schedule is to get you thinking about the project. Plotting the project to completion helps you visualize the finished project and transforms it from an abstract idea to something more tangible and doable. The schedule for Jompanion is based on the materials developed in earlier stages of the project.

After you develop a schedule, reevaluate the project specifications. Are the constraints, needs, and functionality determined earlier realistic given the timetable you have developed? If the answer is yes, you can go on to designing the project.

If the answer is no, carefully review the project and see what you can modify. Sometimes this might mean going back to management and asking for more time. Other times it might mean postponing the development of nonessential functions.

## Project Design

During the design phase, you use the preliminary materials developed in the previous phases to design the application. Because Java is an object-oriented language, it seems logical that you use object-oriented design methodology to create Java applications. The goal of object-oriented design is to determine the objects in an application and then to design the application in terms of those objects.

If you take a close look at the event list developed earlier in the section titled "Defining the Necessary Objects," you should see that it translates into a series of objects needed in the application. Start by grouping the events into object categories, such as functions and user interfaces. Then consider which object groups should be added to the main window. For Jompanion, these objects include a menu bar, choice menus, a text area, and the file you are editing. There must be containers for these objects in the application.

You also need to allow users to manipulate the main window of the application. In most applications, this is a feature of the method that displays the window. This method should describe either the attributes of the window or the call functions that describe those attributes. Some of the attributes you should consider for the main window of any application include

- Initial screen size
- Color of the screen and borders
- Color, size, and font type of text on the screen
- Location of window-manipulation mechanisms
- Style of the title bar
- Location of the menu bar
- Size of the text area

The next object to design is a menu bar with several menus. You can determine the type of menus by grouping the functions of the editor into common categories, as shown in Table 21.1.

Table 21.1. The `MenuBar` object.

Object | Component
Menu bar | File menu
| Edit menu
| About menu

Each menu component contains menu items, which are objects that perform specific operations. These operations are driven by the events defined earlier in this chapter and help to define the methods needed in the application. Table 21.2 shows these operations.

Table 21.2. Menu functions.

Object | Menu Item | Purpose
File menu | New | Create a new file
| Open | Open a file
| Save | Save current file
| Save As | Save current file with a new name
| Exit | Exit the editor
Edit menu | Find | Find a keyword or phrase
| Replace | Replace a keyword or phrase
| Replace All | Replace all occurrences of a keyword or phrase
| Cut | Cut text and save to a buffer
| Copy | Copy text and save to a buffer
| Paste | Paste text from the buffer
About menu | About | Provide help or overview information

In Java, events the user can cause by clicking a mouse button are generally called action events. A set of related events, called key actions, are events that occur when the user presses a key. As you design the application, you should consider mapping certain key actions to events. To make the Jompanion editor easier to use, I decided to map most events to the function keys, thus letting the user press a function key to initiate an event. Table 21.3 shows how I mapped the function keys to events.

Table 21.3. Mapping key actions to events.

Function Key | Related Event
f1 | Open
f2 | Save
f3 | Save As
f4 | Find
f5 | Replace
f6 | Replace All
f7 | Cut
f8 | Copy
f9 | Paste

The next two objects in the application are the choice menus that allow users to select the size and type of the font. (See Table 21.4.) When a user makes a selection, all text in the current document is displayed in the new font type or size.

Table 21.4. Choice menus.

Selection Menu | Selection Menu Choices
Font Type | Courier
| System
| Helvetica
| Times Roman
Font Size | 6
| 7
| 8
| 9
| . . .
| 24

So far, I have accounted for all but two of the events specified earlier, but the application still lacks a mechanism to inform users when errors occur. Most applications display errors in pop-up windows, which sounds like a good idea. This revelation, however, leads to the question of where in the application other pop-up windows might be needed and what purpose they will serve. Pop-up windows that display information or accept user input are generally called dialog boxes; Table 21.5 shows the list of dialog boxes to be used in the Jompanion application.

Table 21.5. Dialog boxes for Jompanion.

Dialog Box | Description
Error | Informs the user when an error occurs
Find | Lets the user enter search text
Help | Displays quick keys for the editor
Open File | Allows the user to open a file
Replace | Lets the user enter text to search for and the text with which to replace it
Replace All | Lets the user replace all occurrences of a word or phrase
Save File | Allows the user to save a file

Now that you have identified the major objects used in the application, you can create a list of them. As you implement each object in the next phase, you can check off the object from the list to be sure that you have accounted for all the major functions of the application.

> Key Object List:

- About dialog box
- About menu item
- Choice menu for font size
- Choice menu for font type
- Copy menu item
- Cut menu item
- Error dialog box
- Exit menu item
- `File` object
- Find dialog box
- Find menu item
- Main window
- Menu bar
- New menu item
- Open File dialog box
- Open menu item
- Paste menu item
- Replace All dialog box
- Replace All menu item
- Replace dialog box
- Replace menu item
- Save As menu item
- Save File dialog box
- Save menu item
- Text-editing area

After mapping out most of the objects and events for the application, you can go on to more detailed design. Here you should work out the interaction between objects and determine the arguments objects will accept and pass. When you are comfortable with the design of the application and can visualize the flow of data from object to object, you can begin the implementation phase. The next section provides a detailed description of how Jompanion was transformed from a concept to a completed application.

## Project Implementation

In the implementation phase, you create the source code for the project. For most advanced projects, this phase is actually a combined phase involving teams of programmers who implement and integrate the source code. Generally, each programming team is responsible for creating a specific set of objects with related functionality, such as the user interface or file I/O. After these object sets are created, they are integrated into the application.

The Java programming language shakes up this methodology by allowing programming teams to focus on higher-level functions, which can cut hundreds of hours off any advanced project. As you saw in the "Project Design" section, the Jompanion editor is a fairly advanced project, especially when you consider it has a completely graphical user interface. If Jompanion were programmed in C, the source code would easily run 10,000+ lines. Thanks to the class libraries in the Java API, the source code for Jompanion is just over 800 lines.

The sections that follow show you how to build the Jompanion editor following the design I have described. Step by step, each of the objects identified in the design is implemented. As with any program created in an object-oriented programming language, the key objects identified in the design phase need containers so they can be used throughout the program. The next section details those containers.

### Containers for the Objects

One purpose of the design phase is to help you identify objects needed in the application. In the source code, you must create an instance of these objects before you can use them. You do this by declaring containers for the objects as instance variables. Most instance-variable declarations follow the main class declaration.

Whenever you use instance variables, you should try to determine if initial or default values are needed. In Jompanion, 20 of the key containers follow the Jompanion class declaration:

> `//container for the editing window in the application
> TextArea textArea;
> //container for the choice menu for font type
> //container for the size of the font as a string
> String fontSize;
> //container for the name of the current font
> String fontName = "Courier";
> //container for the size of the font as a number
> int fontNumeric = 12;
> Choice fontChoice;
> //container for the choice menu for font size
> Choice sizeChoice;
> //container for the file object
> private String fileName = null;
> //container for the about dialog box
> private AboutDialog aboutDialog = null;
> //container for the error dialog box
> private ErrorDialog errorDialog = null;
> //container for the find dialog box
> private FindDialog findDialog = null;
> //container for the replace dialog box
> private ReplaceDialog replaceDialog = null;
> //container for the replace all dialog box
> private ReplaceAllDialog replaceAllDialog = null;
> //container for the open file dialog box
> private FileDialog openDialog;
> //container for the save file dialog box
> private FileDialog saveDialog;
> //container for the about menu item
> private MenuItem aboutMenuItem;
> //container for the copy menu item
> private MenuItem copyMenuItem;
> //container for the cut menu item
> private MenuItem cutMenuItem;
> //container for the exit menu item
> private MenuItem exitMenuItem;
> //container for the find menu item
> private MenuItem findMenuItem;
> //container for the new menu item
> private MenuItem newMenuItem;
> //container for the open menu item
> private MenuItem openMenuItem;
> //container for the paste menu item
> private MenuItem pasteMenuItem;
> //container for the replace all menu item
> private MenuItem replaceAllMenuItem;
> //container for the replace menu item
> private MenuItem replaceMenuItem;
> //container for the save as menu item
> private MenuItem saveAsMenuItem;
> //container for the save menu item`

Naturally, a text editor needs objects associated with the direct manipulation of text. Although the objects associated with the menus and dialog boxes are used to display the graphical user interface, they do not directly manipulate the text. You have already identified the key functions for manipulating text; now all you need to do is define containers to hold the objects used to manipulate text. These containers include the following:

> `//container for the text you are copying
> String copyString = "";
> //container for the text you are searching for
> String findString = "";
> //container for the text you are replacing the search text with
> String replaceString = "";
> //container for the size of the font as a string
> String fontSize = "";
> //container for the name of the current font
> String fontName = "Courier";
> //container for the size of the font as a number`

### Building the Main Window

The way you design and implement the main window of your application is extremely important. Users do not want to stare at a screen with colors that hurt their eyes, nor do they want to go through three or four menu levels to perform core functions such as copying text or opening a file. Users also do not like applications that are frustrating to use. For this reason, your main window should

- Be visually appealing
- Provide quick access to all major functions of the application
- Be friendly and logically organized

As you add features to the application's main window, you should keep these concepts in mind. The key features for Jompanion's main window were identified in the design phase, including a menu bar, two choice menus, a text area, and a file object. Earlier, you should have also identified key attributes for the main window, such as the color of the background and text, the initial font type and size, and the size of the window.

The `Jompanion()` method shown in Listing 21.1 sets up the main window for the Jompanion editor. A call to `super` places the title bar on the window. The background color is set to white. The font size and type are set to the default values for the `fontName` and `fontNumeric` objects.

> Listing 21.1. The `Jompanion()` method.

> `/**
> * This method Adds all the essential object interfaces to the application
> * and displays the application as well.
> */
> public Jompanion() {
> super("Jompanion
> ÂWritten by William R. Stanek (c) 1996");
> setBackground(Color.white);
> setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> AddMenu();
> AddChoice();
> AddTextArea();
> AddDialog();
> Dimension d;
> d = Toolkit.getDefaultToolkit().getScreenSize();
> resize(d.width, d.height);
> show();
> textArea.requestFocus();
> }`

Next, the key objects are added to the screen by calling their associated methods. You should always call these objects in the order you want them to be placed on the screen. The `AddMenu()` method adds the menu bar to the window. The `AddChoice()` method adds the two choice menus to the window. The `AddTextArea()`method adds the text area to the window. The `AddDialog()` method sets up the file I/O dialog box for `File` objects.

After the key objects are constructed, the window size is set to the current screen size. This is done by obtaining the dimensions for the user's screen and then resizing the main window to the screen size. You can also use this technique to set the initial window size proportional to the user's screen size. The final step is to display the main window. Figure 21.2 shows Jompanion's main window.

Figure 21.2 : Jompanion's main window.

### Adding the Menu Bar with Pull-down Menus

The first step in creating the objects used in Jompanion's main window is to build the menu bar. The menu bar contains pull-down menus from which users can make selections. Although you can put as many pull-down menus as you want on a single menu bar, you should try to limit the number of pull-down menus to 10 or less. By the same token, you should also try to limit the number of menu items on any single pull-down menu.

When determining the number of items for a menu, keep in mind that you should balance the length of the menus if possible. A good rule of thumb is to limit menu items to roughly two times the number of menus. Thus, instead of building a menu bar with three pull-down menus and roughly 30 menu items per pull-down, you could create a menu bar with seven pull-down menus and roughly 14 menu items per pull-down.

As shown in Listing 21.2, the menu bar for Jompanion was created by logically grouping the necessary functions of the editor into three menus. The first menu, called File, contains file-related menu items. The second menu, Edit, contains all the editing functions. The third menu, About, contains a menu item that provides information on function-key assignments. Although you should try to balance the length of items in menus, for some menus like Jompanion's About menu it doesn't make sense to add other items to the menu.

To make the menu more readable and quicker to scan, menu items should be logically grouped and ordered into subcategories as well. You can separate subcategories in a pull-down menu using the `addSeparator()` method, which adds a graphical rule to the menu that serves to visually separate categories of menu items. Jompanion's File menu is divided into three subcategories of file-related functions with two separators. Similarly, the Edit menu is divided into two subcategories of editing functions with one separator.

After all menus and menu items are added to the menu bar, the menu bar is set for display using the `setMenuBar()`method. Keep in mind that the menu bar is not actually displayed until the `show()` method is invoked. Figure 21.3 shows Jompanion with the Edit menu activated.

Figure 21.3 : Jompanion's Edit menu.

> Listing 21.2. Adding the menu bar.

> `/**
> * Menu Creation Routines:
> * File: New, Open (f1), Save (f2), Save As (f3), Exit
> * Edit: Find (f4), Replace (f5), Replace All (f6),
> * Cut (f7), Copy (f8), Paste (f9)
> * About: About Jompanion
> */
> private void AddMenu() {
> Menu menu;
> MenuBar menuBar = new MenuBar();
> //Sets up the File menu
> menu = new Menu("File");
> newMenuItem = new MenuItem("New");
> menu.add(newMenuItem);
> openMenuItem = new MenuItem("Open (f1)");
> menu.add(openMenuItem);
> menu.addSeparator();
> saveMenuItem = new MenuItem("Save (f2)");
> menu.add(saveMenuItem);
> saveAsMenuItem = new MenuItem("Save As (f3)");
> menu.add(saveAsMenuItem);
> menu.addSeparator();
> exitMenuItem = new MenuItem("Exit");
> menu.add(exitMenuItem);
> menuBar.add(menu);
> //sets up the Edit menu
> menu = new Menu("Edit");
> findMenuItem = new MenuItem("Find (f4)");
> menu.add(findMenuItem);
> replaceMenuItem = new MenuItem("Replace (f5)");
> menu.add(replaceMenuItem);
> replaceAllMenuItem = new MenuItem("ReplaceAll (f6)");
> menu.add(replaceAllMenuItem);
> menu.addSeparator();
> cutMenuItem = new MenuItem("Cut (f7)");
> menu.add(cutMenuItem);
> copyMenuItem = new MenuItem("Copy (f8)");
> menu.add(copyMenuItem);
> pasteMenuItem = new MenuItem("Paste (f9)");
> menu.add(pasteMenuItem);
> menuBar.add(menu);
> //sets up the About menu
> menu = new Menu("About");
> aboutMenuItem = new MenuItem("About Jompanion");
> menu.add(aboutMenuItem);
> menuBar.add(menu);
> setMenuBar(menuBar);
> }`

### Adding the Choice Menus

The second step in creating the objects used in Jompanion's main window is to create the two choice menus identified in the design phase. Unlike menus that are added to an application via a menu bar, choice menus are added as individual objects. Although you could group the choice menus on a panel, the choice menus used in Jompanion are added directly to the main window. This is done by declaring instances of the Choice() method for the fontChoice and fontSize menu objects, adding items to the menus, and then using the add() method to add the choice menus to the main window.

Tip
If you have not set the layout for the current window, the choice menus will not display. Although you could set the layout in Jompanion's `addChoice()` method, the layout is currently set in the `addText()` method. Either way, the choice menus and the text area are placed onto the screen with the same layout.

When you design a choice menu, you should use clear organization. Notice in Listing 21.3 that Jompanion uses both an alphabetic and a numeric list. Generally, choice menus with alphabetic lists should be in alphabetic order, and choice menus with numeric lists should be in numeric order.

> Listing 21.3. Adding the choice menus.

> `/**
> * AddChoice creates the choice boxes for font type and size.
> * Once the choice boxes are built, they are added to the application.
> */
> private void AddChoice() {
> //set up choice box for font type
> fontChoice = new Choice();
> fontChoice.addItem("Courier");
> fontChoice.addItem("Helvetica");
> fontChoice.addItem("System");
> fontChoice.addItem("TimesRoman");
> fontChoice.select(0);
> add(fontChoice);
> //set up choice box for font size
> sizeChoice = new Choice();
> sizeChoice.addItem("8");
> sizeChoice.addItem("10");
> sizeChoice.addItem("12");
> sizeChoice.addItem("14");
> sizeChoice.addItem("16");
> sizeChoice.addItem("18");
> sizeChoice.addItem("20");
> sizeChoice.addItem("22");
> sizeChoice.addItem("24");
> sizeChoice.addItem("26");
> sizeChoice.addItem("28");
> sizeChoice.addItem("30");
> sizeChoice.select(2);
> add(sizeChoice);
> }`

You should always set appropriate default values that mirror the initial settings for the application, especially when the menu affects the screen display. If the default values are not set, the first choice in the menu is displayed. The default value for the `fontChoice` menu object in Jompanion is set to Courier, and the default value for the `fontSize` menu object is set to 12. These defaults mirror the initial settings for font type and font size specified in the variable declarations. The `select()` method is used to set the default value.

### Adding the Text Area

The third step in creating the objects used in Jompanion's main window is to build the text area. This is done using the `TextArea()` method. Because the `TextArea()` method only allows you to set the size of the text area in terms of rows and columns, the optimal size of the text area is difficult to determine and ultimately depends on the display mode of the end-user's computer and the font type and size being used.

Jompanion sets the size of the text area to 24 rows and 80 columns. For users with a 640¥480 display mode or a 13" Macintosh screen, 24 rows by 80 columns is probably a good size for the text area. However, this means that the text area will not fill the screen on computer systems with larger display modes. Because the text area is for editing and previewing text files, the boolean value `SetEditable` is set to `true` before the text area is displayed.

The method in Jompanion that calls the `TextArea()` method is called `AddTextArea`, which is defined as follows:

> `/**
> * AddTextArea creates the text area for your edit sessions.
> */
> private void AddTextArea() {
> setLayout(new FlowLayout(FlowLayout.LEFT));
> textArea = new TextArea(24, 80);
> textArea.setEditable(true);
> add(textArea);
> }`

### Adding `File` Objects

The final step in creating the objects used in Jompanion's main window is to initialize the dialog boxes for opening and saving files. This is done by creating new instances of `FileDialog`. The first dialog box is set with a value of `LOAD`, which allows the user to open files using the `FileDialog` method and its associated pop-up window. The second dialog box is set with a value of `SAVE`, which allows the user to save files using the `FileDialog` method and its associated pop-up window.

The method in Jompanion that calls the `FileDialog()` method is called `AddDialog` and is defined as follows:

> `/**
> * AddDialog initializes the dialog boxes for opening and saving files.
> */
> private void AddDialog() {
> openDialog = new FileDialog(this, "Open File",FileDialog.LOAD);
> saveDialog = new FileDialog(this, "Save File",FileDialog.SAVE);
> }`

### Setting the Wheels in Motion

Although the `Jompanion()` method builds the main window and calls the methods that add objects to the window, it is not the method that sets the whole application in motion. This task is handled by the `main()` method, which is the first method invoked when any application starts. The purpose of Jompanion's `main()` method is to call the `Jompanion()` method.

To increase the functionality of the application as a whole, you can allow the application to accept a filename as a parameter. For this reason, Jompanion's `main()` method lets you pass the name of a file you want to open when the application starts. In addition to being able to open a file in the current directory simply by entering the filename, you can also specify the path to the file when you invoke Jompanion from the command line.

The code for the `main()` method follows:

> `//the main method of the application invokes the Jompanion method
> public static void main(String args[]) {
> Jompanion n = new Jompanion();
> if (args.length == 1)
> if (n.read(args[0])) n.fileName = args[0];
> }
> }`

All events that occur after the application starts and the main window displays are user driven. User-driven events occur when the user makes a selection on a menu, presses a function key, or causes an error.

### Handling Events

The best way to handle events in applications is with the `handleEvent()` method. Most of the events that your application handles should have been identified when you created the event list during the design phase. Normally, appropriate actions for each of these events should be defined in your application's `handleEvent()` method. However, two particular events-error handling and window resizing-are generally not handled at this level. Because error handling is particular to a class or method, events related to errors are usually handled at a lower level. Because window resizing is particular to each window used in the application, window resizing is also handled at a lower level.

Updating the font type and font size based on a choice menu involves converting argument values. For font type, the value of the argument related to the target event is converted to a string. The string contains the name of the font the user selected and is used as an input value for the `Font()` method. Converting the font size is a bit more tricky. First, a string called `fontSize` is set to the value of the target argument; then the string is converted to an integer value using the `parseInt()` method. Because this method throws an exception, the exception is checked for and corrected if it occurs.

The `handleEvent()` method plays a crucial role in the behavior of your application. If there is a flaw in your event-handling logic, your program will not behave as you expect. The first draft of Jompanion's `handleEvent` method for events related to font type and size contains a logic flaw that causes the font setting to not get updated as you would expect. Because the values for font type and font size are set in separate `if...then` statements in the code that follows, the application forgets the value of the current `fontName` object when you set value for the `fontSize` object and forgets the value of the current `fontSize` object when you set the value for the `fontName` object:

> `if(event.target == fontChoice) {
> String fontName = event.arg.toString();
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> }
> if(event.target == sizeChoice) {
> String fontSize = event.arg.toString();
> try {
> fontNumeric = Integer.parseInt(fontSize);
> } catch (NumberFormatException e) {
> fontNumeric = 12; // If error occurs set font size to 12
> }
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> }`

The logic flaw is corrected by setting the `fontName` and `fontSize` values in the same `if...then` statement, as shown in the code sample that follows. To do this, the two related events are combined using a logical `or`. Thus when the user makes a selection on either choice menu, the values for both the `fontName` object and `fontSize` object are set:

> `if(event.target == fontChoice || event.target == sizeChoice){
> String fontName = event.arg.toString();
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> String fontSize = event.arg.toString();
> try {
> fontNumeric = Integer.parseInt(fontSize);
> } catch (NumberFormatException e) {
> fontNumeric = 12; // If error occurs set font size to 12
> }
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> }`

Jompanion's event list includes 16 items. Except for error handling and window resizing, each of these events is handled by the method shown in Listing 21.4. When a user closes the main window, the `WINDOW_DESTROY` event occurs and the program exits. When a user makes a selection from a pull-down or choice menu, an `ACTION_EVENT` event occurs. If the event is an instance of a `MenuItem` object, a method that corresponds to the selected menu item is called, such as `cut()` or `paste()`. If the value associated with the event is an instance of `fontChoice` or `fontSize`, a new font type or size is set for the text area. When a user presses a key, a `KEY_ACTION` event occurs, and the method associated with the key is called.

> Listing 21.4. Handling events from the menu bar.

> `/**
> * Handle Events:
> * Event.WINDOW_DESTROY: ensures clean exit
> * Event.ACTION_EVENT: Events from menu and choice boxes
> * Event.KEY_ACTION: Press function keys
> */
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> System.exit(0);
> case Event.ACTION_EVENT:
> if(event.target instanceof MenuItem) {
> if(event.target == aboutMenuItem) showAboutDialog();
> else if(event.target == copyMenuItem) copy();
> else if(event.target == cutMenuItem) cut();
> else if(event.target == exitMenuItem) System.exit(0);
> else if(event.target == findMenuItem) showFindDialog();
> else if(event.target == newMenuItem) startNewWindow();
> else if(event.target == openMenuItem) openFile();
> else if(event.target == pasteMenuItem) paste();
> else if(event.target == replaceAllMenuItem) showReplaceAllDialog();
> else if(event.target == replaceMenuItem) showRepDialog();
> else if(event.target == saveAsMenuItem) saveAsFile();
> else if(event.target == saveMenuItem) saveOpenedFile();
> }
> if(event.target == fontChoice || event.target == sizeChoice){
> String fontName = event.arg.toString();
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> String fontSize = event.arg.toString();
> try {
> fontNumeric = Integer.parseInt(fontSize);
> } catch (NumberFormatException e) {
> fontNumeric = 12; // If error occurs set font size to 12
> }
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> }
> return(true);
> if(event.target == fontChoice || event.target == sizeChoice){
> String fontName = event.arg.toString();
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> String fontSize = event.arg.toString();
> try {
> fontNumeric = Integer.parseInt(fontSize);
> } catch (NumberFormatException e) {
> fontNumeric = 12; // If error occurs set font size to 12
> }
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> }
> case Event.KEY_ACTION:
> if(event.key == Event.f1) openFile();
> else if (event.key == Event.f2) saveOpenedFile();
> else if (event.key == Event.f3) saveAsFile();
> else if (event.key == Event.f4) showFindDialog();
> else if (event.key == Event.f5) showRepDialog();
> else if (event.key == Event.f6) showReplaceAllDialog();
> else if (event.key == Event.f7) cut();
> else if (event.key == Event.f8) copy();
> else if (event.key == Event.f9) paste();
> }
> return(false);
> }`

### Building the Utility Functions

At the heart of your application is a core set of functions, called utility functions, that perform most of the real work. These functions manipulate the text, graphics, audio, video, or other objects used by your application. For Jompanion, there are two sets of core functions.

The first set of core functions relates to text manipulation. The `cut()` method allows the user to cut selected text and place it in the buffer. This is done by setting a string equal to the value of the selected text and then calling the `replaceText` method of the `textArea` class. The values passed to the `replaceText` method tell it to replace the selected text from start to finish with an empty value, which effectively cuts the text from the current editing session. If you do not set a buffer, your `cut()` method is nothing more than the Delete key on the user's keyboard.

To update the text area after the cut, the `requestFocus()` method is called. The `cut()` method follows:

> `//cuts selected text and places it on the buffer
> public void cut() {
> copyString = textArea.getSelectedText();
> textArea.replaceText("", textArea.getSelectionStart(),
> &nbs p;textArea.getSelectionEnd());
> textArea.requestFocus();
> }`

The second core function allows the user to copy selected text to the buffer. This is done simply by setting a string equal to the value of the selected text. The `copy()`method follows:

> `//copies selected text to the buffer
> public void copy() {
> copyString = textArea.getSelectedText();
> textArea.requestFocus();
> }`

The third core function allows the user to paste text from the buffer. Before you paste an object to the current window, you should check to make sure the object is in the buffer. This is done in the `past()` method by making sure the length of the `copyString` object-the buffer-is greater than zero. If the `copyString` is set to a value, the `insertText` method of the `textArea` class is called. The values passed to the `insertText` method tell it to add the buffer text at the current cursor location.

The `paste()` method follows:

> `//pastes text from the buffer to the screen
> public void paste() {
> if (copyString.length() > 0)
> textArea.insertText(copyString,
> textArea.getSelectionStart());
> textArea.requestFocus();
> }`

The next set of core functions relates to file I/O. The `openFile()` method allows the user to open a text file. This is done by displaying the `openDialog` box initialized earlier as an instance of `fileDialog`. When the user enters a filename, the method checks the validity of the filename and then calls Jompanion's `read()` method. If the file is successfully read, a value is set for the `fileName` variable and the file is displayed in the text area. The `openFile()` method follows:

> `//method for opening files with a specified name
> private void openFile() {
> String filename;
> openDialog.show();
> filename = openDialog.getFile();
> if (filename != null) {
> filename = check(filename);
> if (read(filename)) fileName = filename;
> }
> textArea.requestFocus();
> }`

The Save menu item allows the user to save a currently opened file. For this reason, the next file I/O function depends on a value being set for the `fileName` variable, which means the user opened a file and it is in the editing window. If this value is not set, the `saveOpenedFile()` method invokes the `showErrorDialog` to inform the user that he has made an error. If this value is set, the `saveOpenedFile()` method writes the file to the disk. The code for the method is as follows:

> `//method saves a file you opened previously.
> //method produces error if you did not open a file previously.
> public void saveOpenedFile() {
> if (fileName == null) {
> showErrorDialog("You did not previously open a file. Use Save As.");
> } else {
> write(fileName);
> textArea.requestFocus();
> }
> }`

When the user selects the Save As menu item, the `saveAsFile()` method is called. This method allows the user to save the current editing session to a named file. To do this, the user enters a name for the file in the `saveDialog` box initialized earlier as an instance of `fileDialog`. Provided that a filename is entered and is valid for the system, the current editing session is saved by calling Jompanion's `write()` method. The code for the `saveAsFile()` method follows:

> `//method saves the file you are currently editing
> private void saveAsFile() {
> String filename;
> saveDialog.show();
> filename = saveDialog.getFile();
> if (filename != null) {
> filename = check(filename);
> if (write(filename)) fileName = filename;
> }
> textArea.requestFocus();
> }`

When the user selects the New menu item, the `startNewWindow()` method is called. There are several ways you could start a new window. One way would be to clear the current editing window, as this method does:

> `//creates a new editing session by deleting any previous text
> private void startNewWindow() {
> fileName = null;
> textArea.setText("");
> textArea.requestFocus();
> }\`

The problem with clearing the current window is that the user could possibly lose hours of work simply by selecting the wrong menu item. Therefore, a better way to start a new window is to invoke a new instance of the `Jompanion()` method, which actually does create a new window. This is done as follows:

> `//creates a new editing session by deleting any previous text
> private void startNewWindow() {
> Jompanion n = new Jompanion();
> }`

### Handling File I/O

After opening a File dialog box that allows the user to open or save text files, Jompanion checks the validity of the filename and then invokes related methods for reading and writing files. Jompanion's `read()` and `write()` methods extend corresponding methods in the `java.io` package and return a boolean value that indicates success or failure.

Appropriately, a method called `check` is used to check the validity of filenames. Although the method currently only checks for a bug that appends erroneous characters to the end of the filename, you could easily modify this method to always save files with a particular extension, such as `.java` or `.txt`. The `check()` method follows:

> `//method checks the validity of file names and specifically for a bug
> //fixes the error if it occurs
> private String check(String filename) {
> if (filename.endsWith(".*.*")) {
> filename = filename.substring(0, filename.length()-4);
> }
> return(filename);
> }`

Jompanion's `read()` method, shown in Listing 21.5, reads data input from the buffered input stream one line at a time. Provided that the line contains text, the `read()` method reads the line, inserts a null character at the end of the line, and continues to read until it reaches the end of the file. When the end of the file is reached, the file is closed and the `read()` method returns the boolean value `true` to the caller.

> Listing 21.5. The `read()` method.

> `//method handles reading files from the file system
> private boolean read(String filename) {
> String line;
> FileInputStream in = null;
> DataInputStream dataIn = null;
> BufferedInputStream bis = null;
> StringBuffer buffer = new StringBuffer();
> try {
> in = new FileInputStream(filename);
> bis = new BufferedInputStream(in);
> dataIn = new DataInputStream(bis);
> } catch(Throwable e) {
> showErrorDialog("Can't open \""+filename+"\"");
> return(false);
> }
> try {
> while ((line = dataIn.readLine()) != null) {
> buffer.append(line + "\n");
> }
> in.close();
> textArea.setText(buffer.toString());
> } catch(IOException e) {
> showErrorDialog("Can't read \""+filename+"\"");
> return(false);
> }
> return(true);
> }`

This method catches two errors that occur in different stages of the read process. The first error occurs when Jompanion cannot open the file; the second occurs when Jompanion cannot read a line in the open file. In either case, `read()` calls the `showErrorDialog` object, passes it an appropriate error message, and returns the boolean value `false` to the caller.

Jompanion's `write()` method, shown in Listing 21.6, is very different from the `read()` method. As long as there are characters to read from the text area, the `write()` method writes to a named output file. When the end of the file is reached, the output file is closed and the `write()` method returns the boolean value `true` to the caller.

The `write()` method catches two errors that occur in different stages of the read process. The first error occurs when Jompanion cannot open the output stream file; the second occurs when Jompanion cannot read a line from the text area and write it to the output file. In either case, `write()` calls the `showErrorDialog` object, passes it an appropriate error message, and returns the boolean value `false` to the caller.

> Listing 21.6. The `write()` method.

> `//method handles writing files to the file system
> private boolean write(String filename) {
> FileOutputStream os = null;
> try {
> os = new FileOutputStream(filename);
> } catch (Throwable e) {
> showErrorDialog("Can't write \""+filename+"\"");
> return(false);
> }
> try {
> String s = textArea.getText();
> int len = s.length();
> for (int i=0; i<len; i++) {
> os.write(s.charAt(i));
> }
> os.close();
> } catch(IOException e) {
> showErrorDialog("Can't write \""+filename+"\"");
> return(false);
> }
> return(true);
> }`

### Displaying the Dialog Boxes

As you have probably noticed, the previous sections have not covered the methods for finding and replacing text or for displaying the related dialog boxes. Following the top-down design structure used in Jompanion, you should create the user interface before creating the methods that actually do the work of finding and replacing text. For an advanced project, the best way to create the user interface for dialog boxes is in two stages. First, you create methods that show the dialog boxes; then you create classes or methods that build the dialog boxes.

If you refer back to Jompanion's `handleEvent()` method, you will see that when a user selects a menu item, methods that show the related dialog boxes are called directly if appropriate. Jompanion uses five dialog boxes in all. The dialog boxes for find, replace, replace all, and about are called directly when a user selects a related item from a pull-down menu or presses an appropriate function key. The error dialog box is invoked only when an error occurs.

As you can see in Listing 21.7, the methods for displaying the dialog boxes are very similar. The `showFindDialog()` method checks to make sure it was not called with a null value. If it was, it does not display the dialog box. Otherwise, it checks to see if text is selected in the editing window, uses the selected text as the initial string for the find, and then creates a new instance of Jompanion's `FindDialog` class.

> Listing 21.7. Displaying the dialog boxes.

> `/**
> * Show Dialog Boxes
> * Find, Replace, Replace All, About, and Error
> */
> //displays the find dialog box which has one input field.
> //Currently selected text is placed in the input field.
> public void showFindDialog() {
> if (findDialog != null) findDialog.dispose();
> String sel = textArea.getSelectedText();
> if (sel.length() > 0) findString = sel;
> findDialog = new FindDialog(this, findString);
> findDialog.show();
> findDialog.setFocus();
> }
> //displays the replace dialog box which has two input fields.
> //Currently selected text is placed in the find input field.
> //Previously searched and replaced text is placed in the input field.
> public void showRepDialog() {
> if (replaceDialog != null) replaceDialog.dispose();
> String sel = textArea.getSelectedText();
> if (sel.length() > 0) findString = sel;
> replaceDialog = new ReplaceDialog(this, findString,
> replaceString);
> replaceDialog.show();
> replaceDialog.setFocus();
> }
> //displays the replace all dialog box which has two input fields.
> //Currently selected text is placed in the find input field.
> //Previously searched and replaced text is placed in the input field.
> public void showReplaceAllDialog() {
> if (replaceAllDialog != null) replaceAllDialog.dispose();
> String sel = textArea.getSelectedText();
> if (sel.length() > 0) findString = sel;
> replaceAllDialog = new ReplaceAllDialog(this, findString,
> replaceString);
> replaceAllDialog.show();
> replaceAllDialog.setFocus();
> }
> //displays the about dialog box
> private void showAboutDialog() {
> if (aboutDialog != null) aboutDialog.dispose();
> aboutDialog = new AboutDialog(this);
> aboutDialog.show();
> }
> //displays the error dialog box and the current error
> public void showErrorDialog(String message) {
> if (errorDialog != null) errorDialog.dispose();
> errorDialog = new ErrorDialog(this, message);
> errorDialog.show();
> }`

Although the `showReplaceDialog()`and `showReplaceAllDialog()` methods behave similarly, you should note that new instances of their related classes are passed two strings: `findString` and `replaceString`. Because the `findString` and `replaceString` objects are declared in the `Jompanion` class and are not access restricted, they are accessible to any of the methods and classes in Jompanion. This allows text placed in these strings to be buffered, which provides additional functionality for the user.

When the user finds text using the Find dialog box, the find string is buffered. If the user later selects Replace or Replace All from the Edit menu, the current value of the find string is placed on the appropriate line of the dialog box. However, because the `showReplaceDialog()` and the `showReplaceAllDialog()` methods check for selected text and assign this value to the `findString` object, currently selected text always replaces any previously buffered value for the `findString` object.

Similarly, Jompanion remembers the value of the `replaceString` object, which makes it easier to follow a single replace with a replace all. You should consider using buffers in your applications as well.

Because the `showAboutDialog()` and the `showErrorDialog()` methods do not allow the user to manipulate objects, they are set up differently. The `showAboutDialog()` method simply creates a new instance of Jompanion's `AboutDialog` class and displays it. The `showErrorDialog()` method creates a new instance of Jompanion's `ErrorDialog` class. The string passed to the `ErrorDialog` class contains a message related to the error that occurred.

### Building the Dialog Boxes

Because the objects that build Jompanion's dialog boxes contain several methods and discrete functionality, I created them as class-level objects. Declaring these objects within class structures provides the added advantage of reducing the overhead associated with the dialog boxes until they are actually needed and making it easier to handle the distinct functionality of each dialog box.

#### The `FindDialog` Class

The `FindDialog` class, shown in Listing 21.8, is the first of five related classes that extend the `Dialog` class of the AWT package. The purpose of the `FindDialog` class is to build the Find dialog box, handle related events, and set the focus of the editing window. These three functions are controlled by separate methods. Figure 21.4 shows Jompanion with the Find dialog box active.

Figure 21.4 : Jompanion's Find dialog box.

> Listing 21.8. The `FindDialog` class.

> `/**
> * The FindDialog Class sets up the find dialog box
> * and handles related events.
> */
> class FindDialog extends Dialog {
> Jompanion parent;
> TextField textField;
> public FindDialog(Jompanion parent, String text) {
> super(parent, "Find", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> Label l;
> l = new Label("Find What:");
> p.setLayout(null);
> l.reshape(0,25,110,30);
> p.add(l);
> textField = new TextField(text);
> textField.reshape(111,25,200,30);
> p.add(textField);
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,340,100);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> case Event.ACTION_EVENT:
> if(event.target == textField) {
> dispose();
> if (textField.getText().length() > 0) {
> parent.setFindString(textField.getText());
> parent.findText();
> parent.textArea.requestFocus();
> return(true);
> }
> } else {
> parent.showErrorDialog("Find Error");
> parent.textArea.requestFocus();
> return(true);
> }
> }
> return(false);
> }
> public void setFocus() {
> textField.requestFocus();
> textField.select(0, textField.getText().length());
> }
> }`

As you design your own dialog boxes for your applications, you should consider carefully their style, size, and placement. Generally, the dialog box should have a style that makes it easy to use and read. Its size should be appropriate for the contents of the box and should rarely display at more than half of the current screen size. The dialog box should also display in an appropriate location, such as centered on the current window.

The `FindDialog()` method builds the dialog box as a panel with a single text field labeled with the keywords `Find What`. The style of the Find dialog box is controlled by setting the background color, positioning the label and the text area within the panel so it is easy to read and use, and defining the layout. The size of the Find dialog box is set so that it is just large enough to display the label and the text area. Finally, the dialog box is placed so that it is displayed in the upper portion of the window and to the right of the pull-down menus. To ensure the user cannot resize the dialog box, the boolean value for the `setResizable()` method is set to `false`.

The `handleEvent()` method handles two events: the closing of the dialog box and the pressing of the Enter key with the cursor in the text field. When the user closes the dialog box, the `WINDOW_DESTROY` event occurs and the Find dialog box is closed. Because text fields are a single line of text, pressing the Enter key within the text field causes an `ACTION_EVENT` to occur. To process the related event, check for a target event that is an instance of the `TextField` object. In this class, the instance of the `TextField` object is declared as `textField`.

When an event related to object `textField` occurs, the `handleEvent()` method checks the validity of the input by checking the length of the text in the text field to ensure it is greater than zero. If the length is greater than zero, the `findText()` method of the parent class is called. Otherwise, an error occurs and an appropriate message is displayed by calling the `showErrorDialog()` method of the parent.

Note
Even when the user presses Enter and leaves the text field empty, the length of the text field is greater than zero. This is because the carriage-return and line-feed characters are generally associated with the Enter key. Therefore, it is virtually impossible for an error to occur. (However, a well-designed program checks for all possible errors and handles them appropriately if they occur.)

The final method declared in the `FindDialog` class is the `setFocus()` method. This method shows the location of the text the user searched for and then selects the search text-an easy way to highlight the results of the find.

#### The `ReplaceDialog` Class

Although the design of the `ReplaceDialog` class is very similar to that of the `FindDialog` class, the `ReplaceDialog` class is more complex because it uses multiple input fields. (See Listing 21.9.) The logic used to handle events from multiple text fields is extremely important. If your logic is not precise, the application will not behave as you expect. Figure 21.5 shows Jompanion with the Replace dialog box active.

Figure 21.5 : Jompanion's Replace dialog box.

> Listing 21.9. The `ReplaceDialog` class.

> `/**
> * The ReplaceDialog Class sets up the replace dialog box
> * and handles related events.
> */
> class ReplaceDialog extends Dialog {
> Jompanion parent;
> TextField fromField, toField;
> public ReplaceDialog(Jompanion parent, String fromString, String
> toString) {
> super(parent, "Replace", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> Label l;
> l = new Label("Find What:");
> p.setLayout(null);
> l.reshape(0,25,130,30);
> p.add(l);
> fromField = new TextField(fromString);
> fromField.reshape(131,25,200,30);
> p.add(fromField);
> l = new Label("Replace With:");
> l.reshape(0,60,130,30);
> p.add(l);
> toField = new TextField(toString);
> toField.reshape(131,60,200,30);
> p.add(toField);
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,360,150);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> case Event.ACTION_EVENT:
> if(event.target == fromField) {
> toField.requestFocus();
> return(true);
> } else if(event.target == toField) {
> dispose();
> if (fromField.getText().length() > 0) {
> parent.setFindString(fromField.getText());
> parent.setReplaceString(toField.getText());
> parent.replaceText();
> parent.textArea.requestFocus();
> return(true);
> }
> } else {
> parent.showErrorDialog("Replace Error");
> parent.textArea.requestFocus();
> return(true);
> }
> }
> return(false);
> }
> public void setFocus() {
> fromField.requestFocus();
> fromField.select(0, fromField.getText().length());
> }
> }`

The `ReplaceDialog` class declares two instances of the `TextField` object that tell the application where to search from and the point to search to. The `fromField` object allows the user to input search text. The `toField` object allows the user to input the replacement text.

Before the `handleEvent()` method processes the events related to these fields, it checks the name of the field in which the user pressed Enter. If the event is related to the `fromField` object, the focus is reset to the `toField` object, which allows the user to press Enter to get to the next input field. If the event is related to the `toField` object and the length of the input text is greater than zero, the `replaceText()` method of the parent class is called. Otherwise, an error occurs and an appropriate message is displayed by calling the `showErrorDialog()` method of the parent.

Tip
When you use multiple text fields for input values that are dependent on each other, you should always reset the focus to the next field until the user is in the final input field. When the user is in the final input field, you should then process all input values.

#### The `ReplaceAllDialog` Class

The `ReplaceAllDialog` class is shown in Listing 21.10. This class is almost identical to the `ReplaceDialog` class, but there is one important difference in the way this class functions. Instead of replacing a single instance of the search text, the `ReplaceAllDialog` class replaces all instances of the search text.

The programming trick used to accomplish this involves changing only a single line of the original `ReplaceDialog` class. The line of code reading

> `parent.replaceText();`

is changed to

> `while (parent.replaceText()) ;`

The new line of code says that while the `replaceText()` method returns a `true` value, continue to loop. Therefore, as long as the `replaceText()` method finds occurrences of the search text, the search text is replaced.

> Listing 21.10. The `ReplaceAllDialog` class.

> `/**
> * The ReplaceAllDialog Class sets up the dialog box for replacing
> * all occurrences of a word or phrase and handles related events.
> */
> class ReplaceAllDialog extends Dialog {
> Jompanion parent;
> TextField fromAllField, toAllField;
> public ReplaceAllDialog(Jompanion parent, String fromAllString, String
> toAllString) {
> super(parent, "Replace All", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> Label l;
> l = new Label("Find All:");
> p.setLayout(null);
> l.reshape(0,25,130,30);
> p.add(l);
> fromAllField = new TextField(fromAllString);
> fromAllField.reshape(131,25,200,30);
> p.add(fromAllField);
> l = new Label("Replace With:");
> l.reshape(0,60,130,30);
> p.add(l);
> toAllField = new TextField(toAllString);
> toAllField.reshape(131,60,200,30);
> p.add(toAllField);
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,360,150);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> case Event.ACTION_EVENT:
> if(event.target == fromAllField) {
> toAllField.requestFocus();
> return(true);
> } else if(event.target == toAllField) {
> dispose();
> if (fromAllField.getText().length() > 0) {
> parent.setFindString(fromAllField.getText());
> parent.setReplaceString(toAllField.getText());
> while (parent.replaceText()) ;
> parent.textArea.requestFocus();
> return(true);
> }
> } else {
> parent.showErrorDialog("Replace All Error");
> parent.textArea.requestFocus();
> return(true);
> }
> }
> return(false);
> }
> public void setFocus() {
> fromAllField.requestFocus();
> fromAllField.select(0, fromAllField.getText().length());
> }
> }`

#### The `AboutDialog` Class

The purpose of the `AboutDialog` class (shown in Listing 21.11) is to build the About dialog box, which displays the mapping of the function keys. The About dialog box is designed so that it can be placed in the bottom of the viewing area for easy reference. For this reason, the height of the box is set to 120 pixels and the width of the box is set to 640 pixels. A grid layout is used to align the nine entries for function keys into easy-to-read columns. To ensure the entries are logically ordered when viewed, they are organized by column in the code. Figure 21.6 shows Jompanion with the About dialog box active.

Figure 21.6 : Jompanion's About dialog box.

> Listing 21.11. The `AboutDialog` class.

> `/**
> * The AboutDialog Class sets up the dialog box that displays
> * information about Jompanion and handles related events
> */
> class AboutDialog extends Dialog {
> Jompanion parent;
> public AboutDialog(Jompanion parent) {
> super(parent, "Jompanion from the Complete Guide to Java", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> p.setLayout(new GridLayout(3, 3));
> p.add(new Label("f1 - Open Files"));
> p.add(new Label("f4 - Find"));
> p.add(new Label("f7 - Cut"));
> p.add(new Label("f2 - Save"));
> p.add(new Label("f5 - Replace"));
> p.add(new Label("f8 - Copy"));
> p.add(new Label("f3 - Save As"));
> p.add(new Label("f6 - Replace All"));
> p.add(new Label("f9 - Paste"));
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(50,440,640,120);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> }
> return(false);
> }
> }`

#### The `ErrorDialog` Class

The purpose of the `ErrorDialog` class is to build a dialog box that displays errors. (See Listing 21.12.) To ensure the dialog box gets the reader's immediate attention, it has a yellow background and a bold font.

> Listing 21.12. The `ErrorDialog` class.

> `/**
> * The ErrorDialog Class sets up the dialog box that displays
> * errors to the user and handles the closing of the window
> */
> class ErrorDialog extends Dialog {
> Jompanion parent;
> String message;
> public ErrorDialog(Jompanion parent, String message) {
> super(parent, "Error", true);
> setBackground(Color.yellow);
> this.parent = parent;
> this.message = message;
> Panel p;
> p = new Panel();
> p.add(new Label(message));
> p.setFont(new Font("System", Font.BOLD, 12));
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,420,100);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> }
> return(false);
> }
> }`

### Text-Manipulation Objects

Now that the user interface for finding and replacing text is built, you can define the associated text-manipulation objects. As with other parts of the application, it is easiest if you start with the basic objects and work to the advanced objects. As Listing 21.13 shows, Jompanion includes two basic text-manipulation objects and two advanced ones.

> Listing 21.13. Text-manipulation objects.

> `//sets the find string
> public void setFindString(String str) {
> findString = str;
> }
> //sets the replace string
> public void setReplaceString(String str) {
> replaceString = str;
> }
> //handles the search for text in the find input field
> public void findText() {
> String s;
> int spoint, index;
> textArea.requestFocus();
> if (findString.length() == 0) return;
> s = textArea.getText();
> if (s.length() == 0) return;
> spoint = textArea.getSelectionEnd();
> index = s.substring(spoint).indexOf(findString);
> if (index >= 0) {
> int i = spoint + index;
> textArea.select(i, i + findString.length());
> }
> }
> //handles replacing text
> public boolean replaceText() {
> String s;
> int spoint, index;
> textArea.requestFocus();
> if (findString.length() == 0) return(false);
> s = textArea.getText();
> if (s.length() == 0) return(false);
> spoint = textArea.getSelectionStart();
> index = s.substring(spoint).indexOf(findString);
> if (index >= 0) {
> int i = spoint + index;
> int j = i + findString.length();
> textArea.replaceText(replaceString, i, j);
> textArea.select(i, i+replaceString.length());
> return(true);
> }
> return(false);
> }`

The purpose of the `setFindString()` and `setReplaceString()` methods is simply to set a value for the find and replace strings. These functions are created as separate methods so they can be called from wherever they are needed in the application.

The `findText()`method is called by the `FindDialog` class. The purpose of this method is to search for text entered in the Find input field by setting an index point within the document and searching forward through the document until the next instance of the text is found. The `getSelectionEnd()` method is used to determine the starting location of the index point. By placing the index after the current occurrence of the search text, the user can select text and then find the next occurrence of the selected text. If the index point was placed before the currently selected text using the `getSelectionStart()`method, the application would consider the currently selected text as the next occurrence of the search text, which is flawed logic.

The `replaceText()` method is called by both the `ReplaceDialog` class and the `ReplaceAllDialog` class. The purpose of this method is to search for text entered in the find input field and replace it with text in the replace input field. To ensure a currently selected occurrence of the find text is replaced, the index point for the start of the search is set with the `getSelectionStart()` method. If the index point was placed after the currently selected text using the `getSelectionEnd()` method, the application would never replace an instance of the currently selected text, which is flawed logic.

## The Complete Project

Although Jompanion is a fairly advanced application, its sound design helped to ensure that the development and implementation of the project went smoothly. Listing 21.14 shows the complete code for the Jompanion application. Although typing in the code line by line forces you to study each line of code, you can go straight to the CD-ROM and access the complete source code if you choose.

> Listing 21.14. The finished application.

> `import java.awt.*;
> import java.io.*;
> /**
> * Peter Norton's Guide to Java Programming
> * Jompanion, a companion text editor for Java
> * with many features including search, replace, replace all
> * cut, copy and paste. Jompanion also lets you set font type
> * and size.
> */
> public class Jompanion extends Frame {
> //set up variables for use throughout Jompanion
> //container for the editing window in the application
> TextArea textArea;
> //container for the choice menu for font type
> //container for the size of the font as a string
> String fontSize;
> //container for the name of the current font
> String fontName = "Courier";
> //container for the size of the font as a number
> int fontNumeric = 12;
> Choice fontChoice;
> //container for the choice menu for font size
> Choice sizeChoice;
> //container for the file object
> private String fileName = null;
> //container for the about dialog box
> private AboutDialog aboutDialog = null;
> //container for the error dialog box
> private ErrorDialog errorDialog = null;
> //container for the find dialog box
> private FindDialog findDialog = null;
> //container for the replace dialog box
> private ReplaceDialog replaceDialog = null;
> //container for the replace all dialog box
> private ReplaceAllDialog replaceAllDialog = null;
> //container for the open file dialog box
> private FileDialog openDialog;
> //container for the save file dialog box
> private FileDialog saveDialog;
> //container for the about menu item
> private MenuItem aboutMenuItem;
> //container for the copy menu item
> private MenuItem copyMenuItem;
> //container for the cut menu item
> private MenuItem cutMenuItem;
> //container for the exit menu item
> private MenuItem exitMenuItem;
> //container for the find menu item
> private MenuItem findMenuItem;
> //container for the new menu item
> private MenuItem newMenuItem;
> //container for the open menu item
> private MenuItem openMenuItem;
> //container for the paste menu item
> private MenuItem pasteMenuItem;
> //container for the replace all menu item
> private MenuItem replaceAllMenuItem;
> //container for the replace menu item
> private MenuItem replaceMenuItem;
> //container for the save as menu item
> private MenuItem saveAsMenuItem;
> //container for the save menu item
> private MenuItem saveMenuItem;
> /**
> * This method Adds all the essential object interfaces to the application
> * and displays the application as well.
> */
> public Jompanion() {
> super("Jompanion
> ÂWritten by William R. Stanek (c) 1996");
> setBackground(Color.white);
> setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> AddMenu();
> AddChoice();
> AddTextArea();
> AddDialog();
> Dimension d;
> d = Toolkit.getDefaultToolkit().getScreenSize();
> resize(d.width, d.height);
> show();
> textArea.requestFocus();
> }
> /**
> * Menu Creation Routines:
> * File: New, Open (f1), Save (f2), Save As (f3), Exit
> * Edit: Find (f4), Replace (f5), Replace All (f6),
> * Cut (f7), Copy (f8), Paste (f9)
> * About: About Jompanion
> */
> private void AddMenu() {
> Menu menu;
> MenuBar menuBar = new MenuBar();
> //Sets up the File menu
> menu = new Menu("File");
> newMenuItem = new MenuItem("New");
> menu.add(newMenuItem);
> openMenuItem = new MenuItem("Open (f1)");
> menu.add(openMenuItem);
> menu.addSeparator();
> saveMenuItem = new MenuItem("Save (f2)");
> menu.add(saveMenuItem);
> saveAsMenuItem = new MenuItem("Save As (f3)");
> menu.add(saveAsMenuItem);
> menu.addSeparator();
> exitMenuItem = new MenuItem("Exit");
> menu.add(exitMenuItem);
> menuBar.add(menu);
> //sets up the Edit menu
> menu = new Menu("Edit");
> findMenuItem = new MenuItem("Find (f4)");
> menu.add(findMenuItem);
> replaceMenuItem = new MenuItem("Replace (f5)");
> menu.add(replaceMenuItem);
> replaceAllMenuItem = new MenuItem("ReplaceAll (f6)");
> menu.add(replaceAllMenuItem);
> menu.addSeparator();
> cutMenuItem = new MenuItem("Cut (f7)");
> menu.add(cutMenuItem);
> copyMenuItem = new MenuItem("Copy (f8)");
> menu.add(copyMenuItem);
> pasteMenuItem = new MenuItem("Paste (f9)");
> menu.add(pasteMenuItem);
> menuBar.add(menu);
> //sets up the About menu
> menu = new Menu("About");
> aboutMenuItem = new MenuItem("About Jompanion");
> menu.add(aboutMenuItem);
> menuBar.add(menu);
> setMenuBar(menuBar);
> }
> /**
> * AddChoice creates the choice boxes for font type and size.
> * Once the choice boxes are built, they are added to the application.
> */
> private void AddChoice() {
> //set up choice box for font type
> fontChoice = new Choice();
> fontChoice.addItem("Courier");
> fontChoice.addItem("Helvetica");
> fontChoice.addItem("System");
> fontChoice.addItem("TimesRoman");
> fontChoice.select(0);
> add(fontChoice);
> //set up choice box for font size
> sizeChoice = new Choice();
> sizeChoice.addItem("8");
> sizeChoice.addItem("10");
> sizeChoice.addItem("12");
> sizeChoice.addItem("14");
> sizeChoice.addItem("16");
> sizeChoice.addItem("18");
> sizeChoice.addItem("20");
> sizeChoice.addItem("22");
> sizeChoice.addItem("24");
> sizeChoice.addItem("26");
> sizeChoice.addItem("28");
> sizeChoice.addItem("30");
> sizeChoice.select(2);
> add(sizeChoice);
> }
> /**
> * AddTextArea creates the text area for your edit sessions.
> */
> private void AddTextArea() {
> setLayout(new FlowLayout(FlowLayout.LEFT));
> textArea = new TextArea(24, 80);
> textArea.setEditable(true);
> add(textArea);
> }
> /**
> * AddDialog adds the dialog boxes for opening and saving files.
> */
> private void AddDialog() {
> openDialog = new FileDialog(this, "Open File",FileDialog.LOAD);
> saveDialog = new FileDialog(this, "Save File",FileDialog.SAVE);
> }
> /**
> * Handle Events:
> * Event.WINDOW_DESTROY: ensures clean exit
> * Event.ACTION_EVENT: Events from menu and choice boxes
> * Event.KEY_ACTION: Press function keys
> */
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> System.exit(0);
> case Event.ACTION_EVENT:
> if(event.target instanceof MenuItem) {
> if(event.target == aboutMenuItem) showAboutDialog();
> else if(event.target == copyMenuItem) copy();
> else if(event.target == cutMenuItem) cut();
> else if(event.target == exitMenuItem) System.exit(0);
> else if(event.target == findMenuItem) showFindDialog();
> else if(event.target == newMenuItem) startNewWindow();
> else if(event.target == openMenuItem) openFile();
> else if(event.target == pasteMenuItem) paste();
> else if(event.target == replaceAllMenuItem) showReplaceAllDialog();
> else if(event.target == replaceMenuItem) showRepDialog();
> else if(event.target == saveAsMenuItem) saveAsFile();
> else if(event.target == saveMenuItem) saveOpenedFile();
> }
> if(event.target == fontChoice || event.target == sizeChoice){
> String fontName = event.arg.toString();
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> String fontSize = event.arg.toString();
> try {
> fontNumeric = Integer.parseInt(fontSize);
> } catch (NumberFormatException e) {
> fontNumeric = 12; // If error occurs set font size to 12
> }
> textArea.setFont(new Font(fontName, Font.PLAIN, fontNumeric));
> }
> return(true);
> case Event.KEY_ACTION:
> if(event.key == Event.f1) openFile();
> else if (event.key == Event.f2) saveOpenedFile();
> else if (event.key == Event.f3) saveAsFile();
> else if (event.key == Event.f4) showFindDialog();
> else if (event.key == Event.f5) showRepDialog();
> else if (event.key == Event.f6) showReplaceAllDialog();
> else if (event.key == Event.f7) cut();
> else if (event.key == Event.f8) copy();
> else if (event.key == Event.f9) paste();
> }
> return(false);
> }
> /**
> * Utility Functions:
> * Copy, Cut, Paste
> * Open file, SaveAs, Save
> * New
> */
> //cuts selected text and places it on the buffer
> public void cut() {
> copyString = textArea.getSelectedText();
> textArea.replaceText("", textArea.getSelectionStart(),
> &nbs p;textArea.getSelectionEnd());
> textArea.requestFocus();
> }
> //copies selected text to the buffer
> public void copy() {
> copyString = textArea.getSelectedText();
> textArea.requestFocus();
> }
> //pastes text from the buffer to the screen
> public void paste() {
> if (copyString.length() > 0)
> textArea.insertText(copyString,
> textArea.getSelectionStart());
> textArea.requestFocus();
> }
> //method for opening files with a specified name
> private void openFile() {
> String filename;
> openDialog.show();
> filename = openDialog.getFile();
> if (filename != null) {
> filename = check(filename);
> if (read(filename)) fileName = filename;
> }
> textArea.requestFocus();
> }
> //method saves the file you are currently editing
> private void saveAsFile() {
> String filename;
> saveDialog.show();
> filename = saveDialog.getFile();
> if (filename != null) {
> filename = check(filename);
> if (write(filename)) fileName = filename;
> }
> textArea.requestFocus();
> }
> //method saves a file you opened previously.
> //method produces error if you did not open a file previously.
> public void saveOpenedFile() {
> if (fileName == null) {
> showErrorDialog("You did not previously open a file. Use Save As.");
> } else {
> write(fileName);
> textArea.requestFocus();
> }
> }
> //creates a new editing session by deleting any previous text
> private void startNewWindow() {
> fileName = null;
> textArea.setText("");
> textArea.requestFocus();
> }
> /**
> * Show Dialog Boxes
> * Find, Replace, Replace All, About, and Error
> */
> //displays the find dialog box which has one input field.
> //Currently selected text is placed in the input field.
> public void showFindDialog() {
> if (findDialog != null) findDialog.dispose();
> String sel = textArea.getSelectedText();
> if (sel.length() > 0) findString = sel;
> findDialog = new FindDialog(this, findString);
> findDialog.show();
> findDialog.setFocus();
> }
> //displays the replace dialog box which has two input fields.
> //Currently selected text is placed in the find input field.
> //Previously searched and replaced text is placed in the input field.
> public void showRepDialog() {
> if (replaceDialog != null) replaceDialog.dispose();
> String sel = textArea.getSelectedText();
> if (sel.length() > 0) findString = sel;
> replaceDialog = new ReplaceDialog(this, findString,
> replaceString);
> replaceDialog.show();
> replaceDialog.setFocus();
> }
> //displays the replace all dialog box which has two input fields.
> //Currently selected text is placed in the find input field.
> //Previously searched and replaced text is placed in the input field.
> public void showReplaceAllDialog() {
> if (replaceAllDialog != null) replaceAllDialog.dispose();
> String sel = textArea.getSelectedText();
> if (sel.length() > 0) findString = sel;
> replaceAllDialog = new ReplaceAllDialog(this, findString,
> replaceString);
> replaceAllDialog.show();
> replaceAllDialog.setFocus();
> }
> //displays the about dialog box
> private void showAboutDialog() {
> if (aboutDialog != null) aboutDialog.dispose();
> aboutDialog = new AboutDialog(this);
> aboutDialog.show();
> }
> //displays the error dialog box and the current error
> public void showErrorDialog(String message) {
> if (errorDialog != null) errorDialog.dispose();
> errorDialog = new ErrorDialog(this, message);
> errorDialog.show();
> }
> //method handles reading files from the file system
> private boolean read(String filename) {
> String line;
> FileInputStream in = null;
> DataInputStream dataIn = null;
> BufferedInputStream bis = null;
> StringBuffer buffer = new StringBuffer();
> try {
> in = new FileInputStream(filename);
> bis = new BufferedInputStream(in);
> dataIn = new DataInputStream(bis);
> } catch(Throwable e) {
> showErrorDialog("Can't open \""+filename+"\"");
> return(false);
> }
> try {
> while ((line = dataIn.readLine()) != null) {
> buffer.append(line + "\n");
> }
> in.close();
> textArea.setText(buffer.toString());
> } catch(IOException e) {
> showErrorDialog("Can't read \""+filename+"\"");
> return(false);
> }
> return(true);
> }
> //method handles writing files to the file system
> private boolean write(String filename) {
> FileOutputStream os = null;
> try {
> os = new FileOutputStream(filename);
> } catch (Throwable e) {
> showErrorDialog("Can't write \""+filename+"\"");
> return(false);
> }
> try {
> String s = textArea.getText();
> int len = s.length();
> for (int i=0; i<len; i++) {
> os.write(s.charAt(i));
> }
> os.close();
> } catch(IOException e) {
> showErrorDialog("Can't write \""+filename+"\"");
> return(false);
> }
> return(true);
> }
> //method checks the validity of file names and specifically for a bug
> //fixes the error if it occurs
> private String check(String filename) {
> if (filename.endsWith(".*.*")) {
> filename = filename.substring(0, filename.length()-4);
> }
> return(filename);
> }
> //sets the find string
> public void setFindString(String str) {
> findString = str;
> }
> //sets the replace string
> public void setReplaceString(String str) {
> replaceString = str;
> }
> //handles the search for text in the find input field
> public void findText() {
> String s;
> int spoint, index;
> textArea.requestFocus();
> if (findString.length() == 0) return;
> s = textArea.getText();
> if (s.length() == 0) return;
> spoint = textArea.getSelectionEnd();
> index = s.substring(spoint).indexOf(findString);
> if (index >= 0) {
> int i = spoint + index;
> textArea.select(i, i + findString.length());
> }
> }
> //handles replacing text
> public boolean replaceText() {
> String s;
> int spoint, index;
> textArea.requestFocus();
> if (findString.length() == 0) return(false);
> s = textArea.getText();
> if (s.length() == 0) return(false);
> spoint = textArea.getSelectionStart();
> index = s.substring(spoint).indexOf(findString);
> if (index >= 0) {
> int i = spoint + index;
> int j = i + findString.length();
> textArea.replaceText(replaceString, i, j);
> textArea.select(i, i+replaceString.length());
> return(true);
> }
> return(false);
> }
> //the main method of the application invokes the Jompanion method
> public static void main(String args[]) {
> Jompanion n = new Jompanion();
> if (args.length == 1)
> if (n.read(args[0])) n.fileName = args[0];
> }
> }
> /**
> * The FindDialog Class sets up the find dialog box
> * and handles related events.
> */
> class FindDialog extends Dialog {
> Jompanion parent;
> TextField textField;
> public FindDialog(Jompanion parent, String text) {
> super(parent, "Find", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> Label l;
> l = new Label("Find What:");
> p.setLayout(null);
> l.reshape(0,25,110,30);
> p.add(l);
> textField = new TextField(text);
> textField.reshape(111,25,200,30);
> p.add(textField);
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,340,100);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> case Event.ACTION_EVENT:
> if(event.target == textField) {
> dispose();
> if (textField.getText().length() > 0) {
> parent.setFindString(textField.getText());
> parent.findText();
> parent.textArea.requestFocus();
> return(true);
> }
> } else {
> parent.showErrorDialog("Find Error");
> parent.textArea.requestFocus();
> return(true);
> }
> }
> return(false);
> }
> public void setFocus() {
> textField.requestFocus();
> textField.select(0, textField.getText().length());
> }
> }
> /**
> * The ReplaceDialog Class sets up the replace dialog box
> * and handles related events.
> */
> class ReplaceDialog extends Dialog {
> Jompanion parent;
> TextField fromField, toField;
> public ReplaceDialog(Jompanion parent, String fromString, String
> toString) {
> super(parent, "Replace", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> Label l;
> l = new Label("Find What:");
> p.setLayout(null);
> l.reshape(0,25,130,30);
> p.add(l);
> fromField = new TextField(fromString);
> fromField.reshape(131,25,200,30);
> p.add(fromField);
> l = new Label("Replace With:");
> l.reshape(0,60,130,30);
> p.add(l);
> toField = new TextField(toString);
> toField.reshape(131,60,200,30);
> p.add(toField);
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,360,150);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> case Event.ACTION_EVENT:
> if(event.target == fromField) {
> toField.requestFocus();
> return(true);
> } else if(event.target == toField) {
> dispose();
> if (fromField.getText().length() > 0) {
> parent.setFindString(fromField.getText());
> parent.setReplaceString(toField.getText());
> parent.replaceText();
> parent.textArea.requestFocus();
> return(true);
> }
> } else {
> parent.showErrorDialog("Replace Error");
> parent.textArea.requestFocus();
> return(true);
> }
> }
> return(false);
> }
> public void setFocus() {
> fromField.requestFocus();
> fromField.select(0, fromField.getText().length());
> }
> }
> /**
> * The ReplaceAllDialog Class sets up the dialog box for replacing
> * all occurrences of a word or phrase and handles related events.
> */
> class ReplaceAllDialog extends Dialog {
> Jompanion parent;
> TextField fromAllField, toAllField;
> public ReplaceAllDialog(Jompanion parent, String fromAllString, String
> toAllString) {
> super(parent, "Replace All", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> Label l;
> l = new Label("Find All:");
> p.setLayout(null);
> l.reshape(0,25,130,30);
> p.add(l);
> fromAllField = new TextField(fromAllString);
> fromAllField.reshape(131,25,200,30);
> p.add(fromAllField);
> l = new Label("Replace With:");
> l.reshape(0,60,130,30);
> p.add(l);
> toAllField = new TextField(toAllString);
> toAllField.reshape(131,60,200,30);
> p.add(toAllField);
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,360,150);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> case Event.ACTION_EVENT:
> if(event.target == fromAllField) {
> toAllField.requestFocus();
> return(true);
> } else if(event.target == toAllField) {
> dispose();
> if (fromAllField.getText().length() > 0) {
> parent.setFindString(fromAllField.getText());
> parent.setReplaceString(toAllField.getText());
> while (parent.replaceText()) ;
> parent.textArea.requestFocus();
> return(true);
> }
> } else {
> parent.showErrorDialog("Replace All Error");
> parent.textArea.requestFocus();
> return(true);
> }
> }
> return(false);
> }
> public void setFocus() {
> fromAllField.requestFocus();
> fromAllField.select(0, fromAllField.getText().length());
> }
> }
> /**
> * The AboutDialog Class sets up the dialog box that displays
> * information about Jompanion and handles related events
> */
> class AboutDialog extends Dialog {
> Jompanion parent;
> public AboutDialog(Jompanion parent) {
> super(parent, "Jompanion from the Complete Guide to Java", true);
> setBackground(Color.white);
> this.parent = parent;
> Panel p;
> p = new Panel();
> p.setLayout(new GridLayout(3, 3));
> p.add(new Label("f1 - Open Files"));
> p.add(new Label("f4 - Find"));
> p.add(new Label("f7 - Cut"));
> p.add(new Label("f2 - Save"));
> p.add(new Label("f5 - Replace"));
> p.add(new Label("f8 - Copy"));
> p.add(new Label("f3 - Save As"));
> p.add(new Label("f6 - Replace All"));
> p.add(new Label("f9 - Paste"));
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(50,440,640,120);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> }
> return(false);
> }
> }
> /**
> * The ErrorDialog Class sets up the dialog box that displays
> * errors to the user and handles the closing of the window
> */
> class ErrorDialog extends Dialog {
> Jompanion parent;
> String message;
> public ErrorDialog(Jompanion parent, String message) {
> super(parent, "Error", true);
> setBackground(Color.yellow);
> this.parent = parent;
> this.message = message;
> Panel p;
> p = new Panel();
> p.add(new Label(message));
> p.setFont(new Font("System", Font.BOLD, 12));
> add("Center", p);
> Dimension d;
> d = parent.size();
> reshape(200,50,420,100);
> setResizable(false);
> }
> public boolean handleEvent(Event event) {
> switch(event.id) {
> case Event.WINDOW_DESTROY:
> dispose();
> parent.textArea.requestFocus();
> return(true);
> }
> return(false);
> }
> }`

After you understand how the application works, you should try your hand at upgrading it. Some of the additional features you may want to add to Jompanion include

- Using the `dim()` method to dim functions that are currently inaccessible, such as the Cut, Copy, and Paste menu items that cannot be used until the user selects text. You could implement this upgrade by checking the length of the selected text. A length greater than zero means the user has selected text. A length of zero or less means no text is selected.
- Add more menu items, such as a Close menu item to the File menu and a Help menu item to the About menu. You could create a menu item to close the current file. One way to do this is to simply clear the text area. However, you should check the text area to determine if there is data the user might want to save before the window is cleared. To add a Help menu item, you should develop methods to display and build the dialog box.
- Support for more keys, such as Page Down and Page Up. In addition to support for function keys f1 to f12, Java can also check for other types of keys including Shift; Alt; Ctrl; Meta; Home; End; Page Up; Page Down; and the left-arrow, right-arrow, up-arrow, and down-arrow keys. You can add support for these keys by adding events to Jompanion's `handleEvent()` method and creating methods that handle the events. Methods that change the current position in the text area can be set by moving the current focus within the document.
- Multilevel undo, such as being able to undo a replace all. To implement multilevel undo, you first need to create an array that stores the index to the location of the change within the document and the string that was changed at that location.

Note
The Meta key is used primarily on Sun UNIX workstations. On a Windows 95-compatible keyboard, this key would be your Windows key.

## Summary

Developing advanced applications involves careful planning. For most projects, planning involves mapping out the steps necessary to complete the project and allocating a time period for implementing each step. After you plot out the steps that will take you through project completion, you can group the steps into phases. These phases form the software development life cycle for the application.

A top-down methodology was used to develop and implement the Jompanion editor. Using this type of methodology, high-level objects are developed before the low-level ones. For Jompanion, the first step in design and implementation involved developing the main window. After the main window was developed, you looked at the next level of objects and so on, working your way to the low-level objects that performed the actual manipulation of text. In following this design approach, you used sound object-oriented design techniques and should now be ready to create your own advanced applications in Java.