Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 6 of 6
private void doExit()
{
if (fDirty)
switch (JOptionPane.showConfirmDialog(JPad.this, SAVE_CHNGS,
TITLE_AYS,
JOptionPane.YES_NO_OPTION))
{
case JOptionPane.YES_OPTION: if (doSave()) dispose(); break;
case JOptionPane.NO_OPTION : dispose();
}
else
dispose();
}
JPad presents dialog boxes for selecting a file from the filesystem, showing About information, displaying error messages,
and prompting for yes/no responses. As Listing 9 shows, JPad's constructor instantiates javax.swing.JFileChooser to handle file-selection. fc.setAcceptAllFileFilterUsed(false) then removes the default "All Files" filter.
fc = new JFileChooser(".");
fc.setAcceptAllFileFilterUsed(false);
FileFilter ff = new FileNameExtensionFilter("TXT documents (*.txt)",
"*.txt");
fc.addChoosableFileFilter(ff);
ff = new FileNameExtensionFilter("All Files", "*.*");
fc.addChoosableFileFilter(ff);
The constructor first invokes the JFileChooser(String currentDirectoryPath) constructor with a dot (.) as its argument. The dot signifies that the current directory is to serve as the file chooser's
initially displayed directory.
The constructor then removes the file chooser's default "All Files" file filter. It then registers a pair of filters with
the file chooser, instructing it to accept only files with .txt file extensions, followed by all files (meaning that the extension will be ignored).
A file filter is conveniently described by a javax.swing.filechooser.FileNameExtensionFilter instance, which is registered with the file chooser by invoking JFileChooser's void addChoosableFileFilter(FileFilter filter) method.
The file chooser is later used to display an open or save dialog box in the void doOpen(File file) or boolean doSaveAs() method. Listing 10 demonstrates the file chooser being used in the former method.
if (file == null)
if (fc.showOpenDialog(JPad.this) == JFileChooser.APPROVE_OPTION)
file = fc.getSelectedFile();
if (file == null)
return;
fc.setCurrentDirectory(file.getParentFile());
fc.setCurrentDirectory(file.getParentFile()) ensures that the next file-chooser activation will set the initial directory to the current one.
JPad presents its About dialog box in response to the user selecting "About JPad" from the Help menu. This dialog box is created
and displayed by invoking one of JOptionPane's showMessageDialog() methods, as demonstrated in Listing 11. (Note that icon is a javax.swing.ImageIcon instance previously initialized via icon = new ImageIcon("icon.png");.)
JOptionPane.showMessageDialog(JPad.this,
"JPad 1.0\nby Jeff Friesen\n",
"JPad",
JOptionPane.PLAIN_MESSAGE,
icon);
Figure 2 is a screenshot of JPad's About dialog box.

The newline character (\n) is used to split the message across multiple lines and to leave an extra blank line for padding. showMessageDialog() is also used to display Alert dialog boxes in response to I/O and other errors. Note, too, that one of JOptionPane's showConfirmDialog() methods is used to prompt for yes/no responses (also refer back to Listing 3 and Listing 8).
Users of JPad can access its clipboard via cut, copy, and paste operations. Much of this support is handled automatically
by a Swing JTextArea, which accesses the system clipboard and performs a cut, copy, or paste operation when it detects a specific key combination
(e.g., Ctrl+C).
JTextArea also declares methods such as void copy(), which programmatically access the system clipboard. JPad's menu item listeners for the Edit menu's Cut, Copy, and Paste
menu items execute these methods when an item is selected, as in the case of ta.copy();.
JPad also needs to directly access the system clipboard in order to determine when text is available for pasting. When text isn't available, Edit's Paste menu item must be disabled; otherwise, this menu item is enabled.
The constructor executes the code shown in Listing 12 to access the system clipboard and save its reference in a java.awt.datatransfer.Clipboard field variable.
cb = Toolkit.getDefaultToolkit().getSystemClipboard();
Edit's menu listener executes the code shown in Listing 13 in its menuSelected() method.
boolean b = cb.isDataFlavorAvailable(DataFlavor.stringFlavor);
miPaste.setEnabled(b);
Each time the Edit menu is selected, Clipboard's boolean isDataFlavorAvailable(DataFlavor) method is executed on the system clipboard with DataFlavor.stringFlavor, which describes the text's content type, also known as a MIME type. If the system clipboard contains text, true returns and the Paste menu item is enabled. Otherwise, there is no text on the system clipboard and Paste is disabled.
JPad lets you drag files to and drop them on the text area. Although multiple files can be dragged to and dropped on this UI component, only the first of these files is opened and its content displayed; the other files are ignored. Listing 14 shows how JPad supports drag and drop.
DropTargetAdapter dta;
dta = new DropTargetAdapter()
{
@Override
public void dragOver(DropTargetDragEvent dtde)
{
Transferable tr = dtde.getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (DataFlavor flavor: flavors)
if (flavor.isFlavorJavaFileListType())
{
dtde.acceptDrag(DnDConstants.ACTION_COPY);
return;
}
}
@Override
public void drop(DropTargetDropEvent dtde)
{
try
{
Transferable tr = dtde.getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (DataFlavor flavor: flavors)
if (flavor.isFlavorJavaFileListType())
{
dtde.acceptDrop(DnDConstants.ACTION_COPY);
@SuppressWarnings("unchecked")
List<File> files = (List<File>) tr.getTransferData(flavor);
if (fDirty)
switch (JOptionPane.showConfirmDialog(JPad.this,
SAVE_CHNGS,
TITLE_AYS,
JOptionPane.YES_NO_OPTION))
{
case JOptionPane.YES_OPTION: if (doSave())
doOpen(files.get(0));
break;
case JOptionPane.NO_OPTION : doOpen(files.get(0));
}
else
doOpen(files.get(0));
dtde.dropComplete(true);
um.discardAllEdits();
return;
}
dtde.rejectDrop();
}
catch (Exception e)
{
JOptionPane.showMessageDialog(JPad.this,
"Drop error: "+e.getMessage(),
"Alert",
JOptionPane.ERROR_MESSAGE);
}
}
};
dt = new DropTarget(ta, dta);
JPad's constructor first subclasses java.awt.dnd.DropTargetAdapter, overriding its void dragOver(DropTargetDragEvent dtde) and void drop(DropTargetDropEvent dtde) methods in order to respond to DropTarget drag and drop events.
A DropTarget drag event arises when an item is dragged over a drop target (an entity associated with a component that is the destination of a drag-and-drop operation). JPad overrides dragOver() to ensure that the mouse cursor always indicates a copy operation.
Files that are dragged to a text editor are always copied and never moved -- it just doesn't make sense to erase the original file. If the user initiates a drag as a move operation, JPad ensures that the user receives visual feedback that it's interpreting the drag as a copy operation, so that the user knows the file will not be erased.
A DropTarget drop event arises when the item is released over the drop target. JPad overrides drop() to obtain the first of the list of files being dropped and open it in the text area, after prompting the user to save changes
(when necessary).
Finally, JPad instantiates java.awt.dnd.DropTarget, which defines a drop target that connects the text-area component with the drop-target adapter instance. The text area is
now ready to accept dropped files.
Most text editors offer an undo feature, which enables users to revert recent edits. JPad supports undo by instantiating the javax.swing.undo package's UndoManager class and registering an undoable-edit listener with the text area, as shown in Listing 15.
um = new UndoManager();
UndoableEditListener uel;
uel = new UndoableEditListener()
{
@Override
public void undoableEditHappened(UndoableEditEvent uee)
{
um.addEdit(uee.getEdit());
}
};
ta.getDocument().addUndoableEditListener(uel);
When the text area's document is modified, the registered javax.swing.event.UndoableEditListener's undoableEditHappened() method is invoked with a javax.swing.event.UndoableEditEvent instance, which describes the edit. This instance's UndoableEdit getEdit() method is invoked to return the edit as a javax.swing.undo.UndoableEdit instance, which is then added to the UndoManager by invoking its boolean addEdit(UndoableEdit anEdit) method.
The action listener registered with the Edit menu's Undo menu item executes um.undo(); when selected. Because UndoManager's void undo() method throws javax.swing.undo.CannotUndoException when there are no edits to be undone, this menu item must only be enabled when there are edits. I accomplish this restriction
by executing miUndo.setEnabled(um.canUndo()); in the menuSelected() method of the menu listener that is registered with the Edit menu.
Finally, JPad executes um.discardAllEdits(); to tell the undo manager to discard all undoable edits in response to certain events: creating a new empty document, opening
an existing document file, saving the current document to the current document's file or a new file, or dropping a dragged
file onto the text area. The ability to undo old edits wouldn't make sense in any of these new contexts.
In the second part of this article we'll map the basic UI architecture (content pane, menu system, and event handling) of our Swing notepad application to a JavaFX equivalent: JPadFX.
Jeff Friesen is a freelance tutor and software developer with an emphasis on Java and Android. In addition to writing Java and Android books for Apress, Jeff has written numerous articles on Java and other technologies for JavaWorld, informIT, Java.net, and DevSource. Jeff can be contacted via his website at TutorTutor.ca.
Read more about Core Java in JavaWorld's Core Java section.
More from JavaWorld