Putting Data on the Clipboard
It's sometimes useful to automatically put some data on the clipboard.
For example, if you're expecting the user to copy the data that your
application displays in a text area to some other application, you might
like to put the string directly on the clipboard. Here's how to do it:
You'll needs some imports:
import java.awt.datatransfer.*;
import javax.swing.*;
Declare a data member of your class and initialize it as follows:
ClipboardOwner clipboardOwner = new ClipboardOwner() {
public void lostOwnership(Clipboard clip, Transferable tr) {
// who cares?
}
};
Now suppose you have a string which you want to put into a text area,
and then select the string in the text area, and make it available
on the clipboard:
String s = "whatever";
textArea.setText(s);
textArea.requestFocus(); // seems to be necessary, why?
textArea.selectAll();
Toolkit tk = Toolkit.getDefaultToolkit();
StringSelection st = new StringSelection(s);
Clipboard cp = tk.getSystemClipboard();
cp.setContents(st, clipboardOwner);
The user just needs to Ctrl-V (on a Windows system) into another application.
No Ctrl-C is necessary!
Finish Table Editing
If you allow the user to edit values in a table directly, using a table
cell editor, it is important to make sure the last edit is completed before
the values are saved.
For example, if the table is in a dialog box, and the user edits a table
value using a text field, and clicks on the Ok button without completing the
edit (by clicking on another cell, or pressing Return), then it's possible
that the last entered value will not be saved to the model before the
dialog ok procedure takes place.
Here's how to do it; just put this code at the beginning of the ok
callback (or before accessing the model data in any method):
if (table.isEditing()) {
table.getCellEditor().stopCellEditing();
}
Thin Table Columns in a Scroll Pane
By default, the table will appear with all columns sized down so as to
appear entirely within the scroll pane, and there will be no horizontal scrollbar.
This can be annoying if there's lots of columns, since all the headers and cells
will contain "...".
To make the horizontal scrollbar appear, and give the columns a default with,
use:
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
Multi-line Message Dialogs
Here's the easiest way:
String[] lines = { "First line", "Second line", "Third line" };
JOptionPane.showMessageDialog(this, lines);
See the next topic for more versions of showMessageDialog() in which
you can specify the title and icon type.
JOptionPane Options
Here's how to use some of the showXxxDialog methods of JOptionPane:
import javax.swing.*;
public class Option extends JFrame {
public static void main(String[] args) {
new Option();
}
public Option() {
ImageIcon icon = new ImageIcon("icon.gif");
String[] options = { "Jupiter", "Saturn", "Uranus", "Neptune" };
String initialValue = "Saturn";
int i;
String s;
Object o;
//-------------------------------------------------------------------
// message dialog with a single Ok button
// arguments are: parent, message, title, icon type, icon
// PLAIN_MESSAGE results in no icon displayed
//-------------------------------------------------------------------
JOptionPane.showMessageDialog(this, "Mercury"); // title is "Message", information icon
JOptionPane.showMessageDialog(this, "Mercury", "Planets", JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(this, "Mercury", "Planets", JOptionPane.INFORMATION_MESSAGE);
JOptionPane.showMessageDialog(this, "Mercury", "Planets", JOptionPane.PLAIN_MESSAGE);
JOptionPane.showMessageDialog(this, "Mercury", "Planets", JOptionPane.QUESTION_MESSAGE);
JOptionPane.showMessageDialog(this, "Mercury", "Planets", JOptionPane.WARNING_MESSAGE);
// custom icon - in this case the icon type is ignored
JOptionPane.showMessageDialog(this, "Mercury", "Planets", JOptionPane.QUESTION_MESSAGE, icon);
//-------------------------------------------------------------------
// confirmation dialog with multiple buttons
// arguments are: parent, message, title, option type (e.g. buttons to show), icon type, icon
// return value is one of: OK_OPTION, CANCEL_OPTION, YES_OPTION, NO_OPTION
// return value is CLOSED_OPTION if the user clicked window manager close button
//-------------------------------------------------------------------
// this one produces: title = "Select an Option", question icon, yes/no/cancel buttons
i = JOptionPane.showConfirmDialog(this, "Venus?");
i = JOptionPane.showConfirmDialog(this, "Venus?", "Planets",
JOptionPane.DEFAULT_OPTION);
i = JOptionPane.showConfirmDialog(this, "Venus?", "Planets",
JOptionPane.OK_CANCEL_OPTION);
i = JOptionPane.showConfirmDialog(this, "Venus?", "Planets",
JOptionPane.YES_NO_CANCEL_OPTION);
i = JOptionPane.showConfirmDialog(this, "Venus?", "Planets",
JOptionPane.YES_NO_OPTION);
i = JOptionPane.showConfirmDialog(this, "Venus?", "Planets",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
i = JOptionPane.showConfirmDialog(this, "Venus?", "Planets",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, icon);
//-------------------------------------------------------------------
// confirmation dialog with customizable buttons
// arguments are:
// parent, message, title, option type, icon type, icon, button labels, label of default button
// option type is not used
// return value is 0 to options.length-1, or CLOSED_OPTION if user cancelled
//-------------------------------------------------------------------
i = JOptionPane.showOptionDialog(this, "Mars", "Planets", JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, icon, options, initialValue);
i = JOptionPane.showOptionDialog(this, "Mars", "Planets", JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, null, options, initialValue);
i = JOptionPane.showOptionDialog(this, "Mars", "Planets", JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, icon, options, null);
i = JOptionPane.showOptionDialog(this, "Mars", "Planets", JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, null, options, null);
//-------------------------------------------------------------------
// text dialog
// arguments are: parent, message, title, icon type
// return value is the entered string, or null if the user cancelled
//-------------------------------------------------------------------
s = JOptionPane.showInputDialog(this, "Planet?");
s = JOptionPane.showInputDialog(this, "Planet?", "Saturn");
s = JOptionPane.showInputDialog(this, "Planet?", "Pick a Planet", JOptionPane.QUESTION_MESSAGE);
//-------------------------------------------------------------------
// choice dialog displaying a combo box
// arguments are: parent, message, title, icon type, icon, options, initial value
// return value is the selected option, or null if the user cancelled
//-------------------------------------------------------------------
o = JOptionPane.showInputDialog(this, "Planet?", "Pick a Planet",
JOptionPane.QUESTION_MESSAGE, icon, options, initialValue);
o = JOptionPane.showInputDialog(this, "Planet?", "Pick a Planet",
JOptionPane.QUESTION_MESSAGE, null, options, initialValue);
o = JOptionPane.showInputDialog(this, "Planet?", "Pick a Planet",
JOptionPane.QUESTION_MESSAGE, icon, options, null);
o = JOptionPane.showInputDialog(this, "Planet?", "Pick a Planet",
JOptionPane.QUESTION_MESSAGE, null, options, null);
}
}
Setting the Application Icon
Use the JFrame's setIconImage() method:
JFrame frame = new JFrame("Frump");
frame.setIconImage(new ImageIcon(imgURL).getImage());
A convenient way to manage the icon is to simply include it
in the same directory as the .class file, and use the
getResource() method of the Class class:
class CoolWindow extends JFrame {
public CoolWindow() {
setIconImage(new ImageIcon(
getClass().getResource("cool.gif")).getImage());
...
}
...
}
Easy GridBagLayout
It's really annoying that there's no standard layout manager
that lets you *easily* layout some labels and text fields in
columns that would be suitable for your basic dialog box.
I guess GridBagLayout is the best of a poor bunch, but it is
so painful to use - you have to waste time reading the doc
every time you use it (unless you use twice a day for a
couple of years.
Anyway, here's a brain-dead method that you can just paste
into your class, if you're not concerned about elegance or
maintainability. I find it useful for just rapidly getting
things going. If you're serious, you can always turn it into
a utility method and clean it up.
private void place(int x, int y, int w, int h, double wx, double wy,
String fill, String anchor, Insets insets,
Container p, GridBagLayout gbl, GridBagConstraints gbc,
Component c) {
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = w;
gbc.gridheight = h;
gbc.weightx = wx;
gbc.weighty = wy;
if ("nw".equals(anchor)) gbc.anchor = GridBagConstraints.NORTHWEST;
if ("n".equals(anchor)) gbc.anchor = GridBagConstraints.NORTH;
if ("ne".equals(anchor)) gbc.anchor = GridBagConstraints.NORTHEAST;
if ("w".equals(anchor)) gbc.anchor = GridBagConstraints.WEST;
if ("c".equals(anchor)) gbc.anchor = GridBagConstraints.CENTER;
if ("e".equals(anchor)) gbc.anchor = GridBagConstraints.EAST;
if ("sw".equals(anchor)) gbc.anchor = GridBagConstraints.SOUTHWEST;
if ("s".equals(anchor)) gbc.anchor = GridBagConstraints.SOUTH;
if ("se".equals(anchor)) gbc.anchor = GridBagConstraints.SOUTHEAST;
if ("n".equals(fill)) gbc.fill = GridBagConstraints.NONE;
if ("h".equals(fill)) gbc.fill = GridBagConstraints.HORIZONTAL;
if ("v".equals(fill)) gbc.fill = GridBagConstraints.VERTICAL;
if ("b".equals(fill)) gbc.fill = GridBagConstraints.BOTH;
if (insets != null) gbc.insets = insets;
gbl.setConstraints(c, gbc);
p.add(c);
}
private void build() {
// and here's how to lay out four rows, the first three with
// label and text field, the last with label and list, so that
// the text fields expand horizontally & the list expands
// horizontally and vertically:
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
JPanel p = new JPanel();
p.setLayout(gbl);
Insets i = new Insets(3, 3, 3, 3);
// creation of widgets omitted
place(0, 0, 1, 1, 0, 0, "b", "w", i, p, gbl, gbc, label1);
place(0, 1, 1, 1, 0, 0, "b", "w", i, p, gbl, gbc, label2);
place(0, 2, 1, 1, 0, 0, "b", "w", i, p, gbl, gbc, label3);
place(0, 3, 1, 1, 0, 0, "n", "nw", i, p, gbl, gbc, label4);
place(1, 0, 1, 1, 1, 0, "b", "w", i, p, gbl, gbc, text1);
place(1, 1, 1, 1, 1, 0, "b", "w", i, p, gbl, gbc, text2);
place(1, 2, 1, 1, 1, 0, "b", "w", i, p, gbl, gbc, text3);
place(1, 3, 1, 1, 1, 0, "b", "w", i, p, gbl, gbc, list);
// etc. etc.
}
CardLayout
Create the cards like this:
private final static String CARD1 = "card1";
private final static String CARD2 = "card2";
// etc.
private CardLayout cardLayout;
private JPanel panel;
cardLayout = new CardLayout();
panel = new JPanel(cardLayout);
JPanel card1 = buildCard1();
panel.add(card1, CARD1);
JPanel card2 = buildCard2();
panel.add(card1, CARD2);
// etc.
Then you can set the card to want to be visible with:
cardLayout.show(panel, CARD1);
If you don't want to store a reference to the layout,
you can always do:
private JPanel panel;
panel = new JPanel(new CardLayout());
CardLayout layout = (CardLayout) panel.getLayout();
layout.show(panel, CARD2);
UIManager Defaults
You can globally set defaults for many features of
Swing controls using the UIManager class.
This is perhaps most useful when you need to change
the default font or color for an entire application.
The basic approach is:
UIManager.put(key, value);
As an example, the default foreground color for JLabel
in the Swing Metal look-and-feel often turns out to be
a cheesy blue, which clashes with the black used on buttons
and other component.
You can make all the labels use black text just by
including the following line near the beginning of
the application:
UIManager.put("Label.foreground", Color.black);
The keys in the call to the put method are strings like
"Tree.background", and the values are objects of various
classes, such as Color, Font, String or Integer.
The actual type depends on what's expected for the key.
The set of permissible keys doesn't seem to be defined
anywhere.
However, many of the keys appear to be used consistently
by the built-in look-and-feels.
The following little app prints out the keys and the
expected value type for all defaults:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class UIDefs {
public static void main(String[] args) {
UIDefaults uidefs = UIManager.getLookAndFeelDefaults();
String[] keys = (String[]) uidefs.keySet().toArray(new String[0]);
Arrays.sort(keys);
for (int i = 0; i < keys.length; i++) {
String k = keys[i];
Object v = uidefs.get(k);
if (v instanceof Integer) {
int intVal = uidefs.getInt(k);
System.out.println(k + ": int");
} else if (v instanceof String) {
String strVal = uidefs.getString(k);
System.out.println(k + ": string");
} else if (v instanceof Dimension) {
Dimension dimVal = uidefs.getDimension(k);
System.out.println(k + ": dimension");
} else if (v instanceof Insets) {
Insets insetsVal = uidefs.getInsets(k);
System.out.println(k + ": insets");
} else if (v instanceof Color) {
Color colorVal = uidefs.getColor(k);
System.out.println(k + ": color");
} else if (v instanceof Font) {
Font fontVal = uidefs.getFont(k);
System.out.println(k + ": font");
} else if (v instanceof Border) {
Border borderVal = uidefs.getBorder(k);
System.out.println(k + ": border");
} else if (v instanceof Icon) {
Icon iconVal = uidefs.getIcon(k);
System.out.println(k + ": icon");
} else if (
v instanceof javax.swing.text.JTextComponent.KeyBinding[]) {
javax.swing.text.JTextComponent.KeyBinding[] keyBindsVal =
(javax.swing.text.JTextComponent.KeyBinding[]) v;
System.out.println(k + ": key binding");
} else if (v instanceof InputMap) {
InputMap imapVal = (InputMap) v;
System.out.println(k + ": input map");
} else if (v != null) {
System.out.println(k + ": " + v.getClass().getName());
} else {
System.out.println(k + ": null");
}
}
}
}
Here's the result of running this app:
Typical Defaults
Uneditable Text Fields
Sometimes you want to use a plain, ordinary text field as a label;
that is, you don't want the user to be able to edit it.
You can call setEditable(false) on a JTextField, but that changes
the color and appearance.
Here's a way to make a text field whose content can be modified
programmatically, but not by the user:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class Main extends JFrame {
public static void main(String[] args) {
new Main();
}
public Main() {
JPanel p = new JPanel(new BorderLayout());
final JTextField text = new UneditableText(20);
p.add(text, BorderLayout.NORTH);
JButton button = new JButton("Test");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
text.setText("nummy");
}
});
p.add(button, BorderLayout.CENTER);
getContentPane().add(p);
pack();
setVisible(true);
}
}
class UneditableText extends JTextField {
public UneditableText(int columns) {
super(new SwitchableDocument(), "", columns);
}
public void setText(String s) {
SwitchableDocument doc = (SwitchableDocument) getDocument();
doc.setModifiable(true);
super.setText(s);
doc.setModifiable(false);
}
}
class SwitchableDocument extends DefaultStyledDocument {
private boolean modifiable;
public SwitchableDocument() {
modifiable = false;
}
public void setModifiable(boolean f) {
modifiable = f;
}
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
if (modifiable) super.insertString(offs, str, a);
}
public void remove(int offs, int len) throws BadLocationException {
if (modifiable) super.remove(offs, len);
}
}
It's probably possible to do the same thing by calling setEditable(false),
and then playing with colors and so on, but I couldn't get it to work.
Forcing Text Area to Scroll
If you're using a text area in a scroll pane to show log message,
you probably want it to scroll to the bottom each time a new block
of text is appended. Here's an easy way to do it:
text.append(block);
text.setCaretPosition(text.getDocument().getLength());
Listening for Text Changes
Here's how to listen for changes in a text component:
textArea = new JTextArea();
textArea.getDocument().addDocumentListener(new MyDocumentListener());
class MyDocumentListener implements DocumentListener {
public void insertUpdate(DocumentEvent e) {
// text inserted
}
public void removeUpdate(DocumentEvent e) {
// text removed
}
public void changedUpdate(DocumentEvent e) {
//Plain text components don't fire these events
}
}
Showing HTML with a Stylesheet
The JEditorPane can display HTML.
It can also be configured to use a stylesheet.
The stylesheet support, however, is quite primitive.
Here
is the list of stylesheet attributes that are supported.
It's not clear whether any of the CSS selectors, other than for plain elements, are supported.
For example, id and class selectors don't seem to have any effect.
Many simple settings don't seem to work either (e.g. setting background color for th elements).
Here's some example code:
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.swing.text.html.*;
public class HtmlDisplayer {
public HtmlDisplayer(String pageUrl, String stylesheetUrl) {
try {
StyleSheet ss = new StyleSheet();
ss.importStyleSheet(new URL(stylesheetUrl));
HTMLEditorKit kit = new HTMLEditorKit();
kit.setStyleSheet(ss);
JEditorPane editorPane = new JEditorPane();
editorPane.setEditorKit(kit);
editorPane.setPage(pageUrl);
JScrollPane scroll = new JScrollPane(editorPane);
JFrame frame = new JFrame();
frame.getContentPane().add(scroll, BorderLayout.CENTER);
frame.setSize(600, 600);
frame.setLocation(200, 200);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
JButton Basics
import javax.swing.*;
import java.awt.event.*;
JButton b = new JButton("Exit");
b.setMnemonic('x');
b.addActionListener(new ActionListener(ActionEvent e)) {
public void actionPerformed(ActionEvent e) {
exit();
}
});
b.setEnabled(true);
Handling Window Border Close
When the user closes a window by clicking
on the close button in the window manager border, you probably
want to do the same as if the user pressed the Close
or Cancel button that you've created in the window.
The old mechanism is to add a window listener, and call the same method
that gets called by your button:
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
cancel();
}
});
Since 1.4, JFrame and JDialog provide a setDefaultCloseOperation() method.
It's a bit easier, and works like this:
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
Centering a Frame/Dialog
This ain't too complicated:
public static void centerOnScreen(JFrame f) {
Dimension s = Toolkit.getDefaultToolkit().getScreenSize();
Dimension d = f.getSize();
f.setLocation((s.width - d.width)/2, (s.height - d.height)/2);
}
public static void centerOnScreen(JDialog dlg) {
Dimension s = Toolkit.getDefaultToolkit().getScreenSize();
Dimension d = dlg.getSize();
dlg.setLocation((s.width - d.width)/2, (s.height - d.height)/2);
}
Anti-aliased Drawing
Anti-aliasing improves the quality of shapes drawn to
a window or image:
RenderingHints renderHints =
new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
...
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHints(renderHints);
...
// go ahead and draw something
}
Changing the Font on the Fly
Here's the technique:
javax.swing.plaf.FontUIResource fr = new javax.swing.plaf.FontUIResource(
font.getFontName(), font.getStyle(), font.getSize());
java.util.Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get (key);
if (value instanceof javax.swing.plaf.FontUIResource) {
UIManager.put (key, fr);
}
}
javax.swing.SwingUtilities.updateComponentTreeUI(frame);
You can also just change the font for a particular
type of widget, by checking the values of the keys.
These are "TextField.font", "Button.font", etc - see the
section on UI Defaults.
JList Basics
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
int index;
Object obj;
DefaultListModel model = new DefaultListModel();
model.addElement(obj);
model.setElementAt(obj, index);
model.insertElementAt(obj, index);
model.removeElementAt(index);
int size = model.size();
JList list = new JList(model);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(javax.swing.event.ListSelectionEvent e) {
// if you don't check for adjusting, you get called twice!
if (!e.getValueIsAdjusting()) {
select();
}
}
});
list.addMouseListener(new MouseAdapter()) {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int index = list.locationToIndex(e.getPoint());
doubleClick(index);
}
}
});
JScrollPane scroll = new JScrollPane(list);
// add scroll to the user interface
Object sel = (Object) list.getSelectedValue();
int index = list.getSelectedIndex();
Object[] sels = (Object[]) list.getSelectedValues();
int[] indexes = list.getSelectedIndexes();
list.setSelectedIndex(index);
list.setSelectedIndexes(indexes);
JComboBox Basics
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
JComboBox cb = new JComboBox();
cb.addItem(obj0);
cb.addItem(obj1);
cb.addItem(obj2);
...
JComboBox cb1 = new JComboBox(new Object[] { obj1, obj2, ... });
cb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
select();
}
});
cb.setMaximumRowCount(16); // max # displayed rows
cb.setSelectedIndex(1);
cb.setSelectedItem(obj1);
int i = cb.getSelectedIndex();
Object obj = cb.getSelectedItem();
cb.setEditable(true); // can type in a value