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.

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:

  1. Select "Containers" containers.gif (1114 bytes) from the Bean Palette.
  2. Select "FileDialog" filedialog.gif (1156 bytes).
  3. Add two of these beans to the design area, outside of any other components.
  4. Set their bean names to "loadFileDialog" and "saveFileDialog"
  5. 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. 

  1. "Containers" should still be selected...
  2. Select "Dialog" dialog.gif (1030 bytes).
  3. Add a dialog to the design area, outside of any other components.
  4. Set the dialog's modal property to true.  This means that the dialog must be addressed before the user can do anything else.
  5. 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...)
  6. 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. 

  1. Bring up the pop-up menu for the Ok button.
  2. Select Connect->actionPerformed(...).  This starts a connection that will be triggered when the button is pressed.
  3. Move the mouse (the cursor is now a funky crosshair) to the title bar of the dialog frame, and click.
  4. 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

  1. Add a new Dialog to the design area.
  2. Set the dialog's modal property to true.  This means that the dialog must be addressed before the user can do anything else.
  3. Add a Label to the dialog that says "Enter a value:"
  4. Add a TextField textfield.gif (1017 bytes) 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:

  1. Select "Options->Add bean..."
  2. Type magercises.gui1a.soution.Kernel as the class name and make sure you are adding bean type "class."
  3. Press "Ok".
  4. 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:

  1. Bring up its pop-up menu.
  2. Select Connect->actionPerformed.  This starts an action that is triggered when the menu item is selected.
  3. Click on the title bar of the "Abandon all help..." dialog.
  4. 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" hideconnections.gif (1057 bytes) to clean things up.  At any time, you can click "Show connections" showconnections.gif (1057 bytes) 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 test.gif (1033 bytes).

File->New

This item should just call the newFile() method in the kernel:

  1. For the "New" menuitem in the File menu, select Connect->actionPerformed.
  2. Click on the kernel object you created in the last task.
  3. Select "All features..."
  4. 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.

  1. From our File->Open item, select Connect->actionPerformed.
  2. Click on the loadFileDialog bean.
  3. Select show().
  4. 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.)
  5. Click on the kernel object.
  6. Select "All features..."
  7. 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.
  8. 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.
  9. Click on the loadFileDialog object.
  10. Select "directory."  This associates the directory property of the dialog to the path parameter of the openFile method.
  11. In the same manner, associate the name parameter of the openFile method with the file property of the loadFileDialog bean.
  12. 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:

  1. Connect File->Open's actionPerformed event to saveFileDialog's show() method.
  2. Connect File->Open's actionPerformed event to th kernel's saveAsFile(path, name) method.
  3. Connect the path and name parameters of the last connection to the directory and file properties (respectively) of the saveFileDialog bean.
  4. 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:

  1. Connect the exceptionOccurred event of the connection we just created to the saveFileDialog's show() method.
  2. Connect the same exceptionOccurred to the kernel's saveAsFile(path, name) method.
  3. Connect the path and name parameters to the directory and file properties of the saveFileDialog bean.
  4. 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:

  1. Connect the File->Open audio file's actionPerformed event to the loadFileDialog's show() method.
  2. Connect the same actionPerformed event to the kernel's openAudioFile(path, name) method.
  3. Connect the path and name parameters to the loadFileDialog's directory and path as usual.
  4. Connect the same actionPerformed event to the label whose text is "<none>", selecting the "text" property.  You will be changing this property.
  5. 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...

  1. Connect the actionPerformed event of the Edit->Change interleave ratio to the show() method of the dialog that has the text field.
  2. 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.

  3. Connect the actionPerformed method of the TextField in that dialog to the kernels' setInterleaveRatio() method.  Note that the connection needs a parameter.
  4. 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:
    1. 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.
    2. 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.
    3. 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.
  5. 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.

  1. 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)
  2. 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.)


Copyright © 1996-1997 MageLang Institute. All Rights Reserved.