Help: GUI Connection 1
Help is available for each task, or you can go straight to
the solution source code.
Task 1
Determine the functionality of the application.
Now that you have built the GUI, you need to connect it to the kernel for your
application. The kernel is the part of your application that does all the real work.
Whenever you design an application, try to get as much separation between the GUI
and kernel as possible. Think of the kernel as something that could possible be run
standalone, taking arguments from the command line. You won't always be able to get
that much separation, but it's a good goal and will make your design cleaner.
For this Magercise, you will connect the GUI you created in GUI Conversion 1 to a stub
kernel that is provided. If you were able to finish constructing the GUI, continue
working in package magercises.gui1. If you were not able to finish, there
is a GUI pre-built in package magercises.gui1a.
The functionality to implement will be as follows:
- File->New
- call the kernel method newFile()
- File->Open
- Bring up a file open dialog to get name
- call the kernel method openFile(String name)
- File->Save
- call the kernel method saveFile()
- if it throws an exception
- Bring up a file dialog to get name
- try to save again using saveAsFile(String name)
- File->Save As...
- Bring up a file dialog
- save the file using saveAsFile(String name)
- File->Open Audio File
- bring up file dialog to get name
- set the audio file label to that name
- call kernel method openAudioFile(String name)
- File-Save Audio File
- bring up file dialog to get name
- set the audio file label to that name
- call kernel method saveAudioFile(String name)
- Edit->Change interleave ratio AND Interleave Button
- Bring up a dialog to get the ratio
- call kernel method setInterleaveRatio(int ratio)
- (I know, I know, ratios are usually floats...)
- Edit->Change audio chunk skew AND Skew Button
- Bring up a dialog to get the skew
- call kernel method setAudioChunkSkew(int skew)
- View->Status information
- set the TextArea to the return value of the kernel's getStatus() method.
- Help items
- All help items should bring up a dialog that says "Abandon all help ye who enter
here..."
The rest of the menu options and Buttons should just bring up a dialog that says
"option not yet available".
This means that we need the following extra elements for our GUI:
- A file load dialog
- A file save dialog
- An "option not yet available dialog"
- An "Abandon all help ye who enter here..." dialog
- A dialog that gets a number from the user.
- A kernel object
No additional help available for this task.
Task 2
Add the additional GUI elements that were mentioned in Task 1. See help for details.
First, we'll add the file load dialog:
- Select "Containers"
from the Bean Palette.
- Select "FileDialog"
.
- Add two of these beans to the design area, outside of any other components.
- Set their bean names to "loadFileDialog" and "saveFileDialog"
- Set the mode property of "loadFileDialog" to LOAD and the mode property of
"saveFileDialog" to SAVE.
Next, we'll create the "option not available" dialog.
- "Containers" should still be selected...
- Select "Dialog"
.
- Add a dialog to the design area, outside of any other components.
- Set the dialog's modal property to true. This means that the dialog must be
addressed before the user can do anything else.
- Add a Label to the dialog that says "Option not yet available."
(You should know how to do this by now... Don't bother making it look too
nice with a layout manager; this is a stub anyway...)
- Add a Button to the dialog that says "Ok."
We need to have the "Ok" button do something. So let's make a
connection. We want the OK button to make the dialog go away.
- Bring up the pop-up menu for the Ok button.
- Select Connect->actionPerformed(...). This starts a connection that will be
triggered when the button is pressed.
- Move the mouse (the cursor is now a funky crosshair) to the title bar of the dialog
frame, and click.
- Select dispose(). We've now created a connection that says "when the Ok
button is pressed, dispose the dialog."
Next we want to create the help message. Because this is so similar to the option
dialog we just created, we can copy it by holding the Control key, clicking on the title
bar of the dialog, dragging into an unoccupied area and releasing the mouse. Change
the label text to "Abandon all help ye who enter here...". You'll need to
create the connection for this dialog, as the copy operation does not copy the
connections.
Create another dialog to get a number from the user. Do this by
- Add a new Dialog to the design area.
- Set the dialog's modal property to true. This means that the dialog must be
addressed before the user can do anything else.
- Add a Label to the dialog that says "Enter a value:"
- Add a TextField
to the dialog to the right of the
Label.
Don't add a button to the dialog -- we'll determine the dialog is done when the user
presses Enter. Eventually you should add a Cancel button to the dialog.
We do not add anything else. To continue, the user will need to enter a value and
press Enter. A Cancel Button should be added later when the dialog is formalized.
This one's for testing.
Finally, we add a kernel object:
- Select "Options->Add bean..."
- Type magercises.gui1a.soution.Kernel as the class name and make sure you are
adding bean type "class."
- Press "Ok".
- Click on an unoccupied area to add a kernel bean.
Task 3
Connect the GUI based on the stated behavior.
Take a look at class magercises.gui1a.solution.kernel to see the interface you must connect to.
See help for more details.
Lets take care of the easy ones first. We have several items whose only action at
this point is to bring up one of the message dialogs we created. First, let's deal
with the help menu items.
For each item in the help menu:
- Bring up its pop-up menu.
- Select Connect->actionPerformed. This starts an action that is triggered when
the menu item is selected.
- Click on the title bar of the "Abandon all help..." dialog.
- Select "show()".
For each of the following menu items and buttons, connect them to the "Option not
yet avaiable" dialog in the same manner. If at any time things get too crowded,
click on an empty spot in the design area, then click on "Hide connections" to clean things up. At any time, you can click "Show
connections" to see the connections again.
(Note -- if a component is selected, "Hide connections" or "Show
connections" will apply to only that components.)
- Edit->Generate AVI
- Edit->Merge audio...
- Edit->Split audio...
- Edit->Options
- View->Chunk...
- View->Headers
- View->Index
- View->Packed
- View->RIFF format
- Merge button
- Split button
- Stop button
- View->Decimal
- View->HexaDecimal
For the last two, use the itemStateChanged event instead of actionPerformed. This
is because they are checkBoxMenuItems, and inform us of state changes rather than actions
being performed.
Now would be an opportune time to hide all connections...
Also, we should make a point of saving the bean so we don't lose any work. Select
"File->Save Bean". If you would like, you can test your work so far by
clicking on the "Test" button on the toolbar .
File->New
This item should just call the newFile() method in the kernel:
- For the "New" menuitem in the File menu, select Connect->actionPerformed.
- Click on the kernel object you created in the last task.
- Select "All features..."
- Select "newFile()" under methods, and press "Ok."
Save the bean and test it if you wish.
If you don't see any output when you select File->New on your application,
make sure the VisualAge Console window is visible...
File->Open
We need to get the name of the file to open, so we'll use the loadFileDialog bean we
created earlier.
- From our File->Open item, select Connect->actionPerformed.
- Click on the loadFileDialog bean.
- Select show().
- Select Connect->actionPerformed for File->Open again. This is
adding a second connection on the same event. The actions associated with this event
will be performed in the order in which the connections were added. (There is a
"Reorder connections from..." option for each component that can be used to
change the order if you make a mistake.)
- Click on the kernel object.
- Select "All features..."
- Select "open(String, String)" in the methods pane and press "Ok.".
Notice how the connection that was just created is composed of a dotted line.
This means that the connection needs more information.
- Bring up the pop-up menu for this new connection and select Connect->path.
"Path" is the first parameter to the openFile() call we are setting up.
- Click on the loadFileDialog object.
- Select "directory." This associates the directory property of the dialog
to the path parameter of the openFile method.
- In the same manner, associate the name parameter of the openFile method with the file
property of the loadFileDialog bean.
- Finally, dispose of the loadFileDialog by connecting the File->Open's actionPerformed
event to the loadFileDialog's dispose() method. This resets the dialog so it can be
used again.
Hide all connections if things are getting messy.
File->Save As...
Notice that I am skipping "save" for the moment -- it's a bit trickier.
"Save As..." should act parallel to the "Open" command we just set
up. Set it up the same way with the following connections:
- Connect File->Open's actionPerformed event to saveFileDialog's show() method.
- Connect File->Open's actionPerformed event to th kernel's saveAsFile(path, name)
method.
- Connect the path and name parameters of the last connection to the directory and file
properties (respectively) of the saveFileDialog bean.
- Finally, dispose of the saveFileDialog by connecting the File->Save As's
actionPerformed event to the loadFileDialog's dispose() method. This resets the
dialog so it can be used again.
Make sure they're created in that order...
Save the bean and test it if you wish...
File->Save
Coming back to save, the difference in save is that there might not be a file name set
in the kernel yet. If File->Open or File->SaveAs are used, the kernel will
keep track of the file name that was passed to it. File->new will reset the file
name.
If there isn't a file name set when the saveFile(path, name) method is called, it will
throw a generic RuntimeException (a better idea would be to create a new exception
explicit to this condition, but that is left as an exercise for the reader with too much
time on his or her hands.)
VisualAge provides a nice way to catch that exception. But first, let's just try
to save the file...
Create a connection from File->Save's actionPerformed event to the kernel's
saveFile() method. Note that there are no parameters to this version of
saveFile()...
To handle that exception case:
- Connect the exceptionOccurred event of the connection we just created to the
saveFileDialog's show() method.
- Connect the same exceptionOccurred to the kernel's saveAsFile(path, name) method.
- Connect the path and name parameters to the directory and file properties of the
saveFileDialog bean.
- Finally, dispose of the loadFileDialog by connecting the exceptionOccurred event to the
saveFileDialog's dispose() method. This resets the dialog so it can be used again.
Save the bean, and give it a test... (Note that if you press the "Test"
button, it will automatically save the bean if you haven't explicitly saved it since your
last change.
File->Open audio file...
This option acts exactly like File->Open, but it also sets the audio file name
label:
- Connect the File->Open audio file's actionPerformed event to the loadFileDialog's
show() method.
- Connect the same actionPerformed event to the kernel's openAudioFile(path, name) method.
- Connect the path and name parameters to the loadFileDialog's directory and path as
usual.
- Connect the same actionPerformed event to the label whose text is
"<none>", selecting the "text" property. You will be
changing this property.
- Connect the value parameter of the last connection to the kernel's
getFullAudioPathName() method.
File->Save audio file...
This option works the same as the Open audio file... option. The only difference
is that you will be using the saveFileDialog to get the file name, and the
saveAudioFile(path, name) method in the kernel.
Make sure you save the bean so far!
Edit->Change Interleave Ratio / Interleave Button
Both this menu option and the Interleave Button perform the same function. First,
we must get a number from the user, then pass it to the kernel for investigation...
- Connect the actionPerformed event of the Edit->Change interleave ratio to the show()
method of the dialog that has the text field.
- Be nice to the user by having the TextField request focus.
Because the dialog is a modal dialog, we cannot simply add another connection from
actionPerformed of the menu item or button; the dialog blocks any further action
until it is disposed.
To add thism you need to connect the WindowOpened event of
the dialog to the requestFocus() method of the TextField.
- Connect the actionPerformed method of the TextField in that dialog to the kernels'
setInterleaveRatio() method. Note that the connection needs a parameter.
- We can't directly set up a connection between the textfield value and the parameter
because the TextField value is a String, and the parameter is an int -- we need to convert
it:
- Add a new Integer variable to the design. Do this by selecting Options->Add
bean..., making the class name java.lang.Integer, selecting "variable" for the
bean type and pressing Ok.
- Connect the interleaveRatio paramater of the setInterleaveRatio() connection to the new
Integer bean's parseInt(String) method. (You'll need to select "All
features" of the Integer variable to see it.) Note that because this is a
static method, we don't need to have the variable holding a valid reference. The
only reason we're using a variable is to easily access the Integer class' static parseInt
method.
- parseInt(String) needs a string, and guess where we'll get it: Connect the "s"
parameter of the parseInt connection to the text property of the TextField.
- Finally, have the actionPerformed event of the TextField dispose() the dialog.
The Interleave button now has it easy. All it has to do is show that dialog and
have the TextField requestFocus().
Edit->Change Audio Chunk Skew / Skew Button
There's only one problem with the way we handled the previous dialog -- the dialog
itself performs the action, tying it forever to changing the Interleave status. Uh
oh. We messed up. Let this serve as a lesson in planning. Dialogs like
this that may serve multiple purposes should not actually perform actions. They
should act just like the FileDialog does. It should get a value from the user and
then hide() itself. The action that show()ed it can then query it for the details
that the user entered.
Because we've already messed up, and I don't feel like typing the other way to do it as
well, we'll stick with the current scheme. but what do we do with these items that
need the same dialog?
Simple -- copy the dialog.
Hold the control key, click on the title bar of the dialog with the text field, and
drag to a clear spot, assuming you can find one...
Remember that the component composition is copied, but not the connections.
That's what we wanted anyway...
Connect the Edit->Change Audio Chunk Skew and the Skew Button to this new dialog in
the same manner you did for the Interleave ratio controls. The only difference is
that you will use the setAudioChunkSkew(int) method of the kernel. (You can use the
same Integer variable that you created for the other controls as well -- remember, you're
just abusing it to access Integer's static parseInt(String) method...
View->Status Information
The final piece of the puzzle. This option should set the value of the TextArea
to the String returned from the kernel's getStatus() method.
- Connect the actionPerformed event of the View->Status Information item to the
TextArea's text property. (Remember, the TextArea is in the center of the main
Frame)
- Connect the value parameter of this connection to the status propery of the kernel.
Save and test the bean!
Dialog Locations
You probably have noticed that the dialogs are coming up in weird spots. To
correct this, open the property sheet for a dialog, click on it's constraints propery,
and set the x and y values to "0".
Possible Extension for Homework...
You might want to set it up so that any action will also perform the "Status
Information" function as well.
Add Cancel buttons to the dialogs and ensure they work properly. (This can be trickier
than it first appears. Exceptions may be helpful here.) |
Add a handler for window location changes. If the window location changes, the location
of the dialogs should change as well.
You may notice that the main Frame does not have focus after a dialog has been used
for input and has been disposed. Add connections that will make the main Frame request
focus after each event that triggers a dialog.